<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Markdown Tools Blog</title>
  <subtitle>Guides and tutorials for writing Markdown everywhere it shows up.</subtitle>
  <link href="https://www.markdowntools.com/blog/feed.xml" rel="self" type="application/atom+xml"/>
  <link href="https://www.markdowntools.com/blog" rel="alternate" type="text/html"/>
  <id>https://www.markdowntools.com/blog/feed.xml</id>
  <updated>2026-10-02T00:00:00+00:00</updated>
  <entry>
    <title>How to Remove Markdown Formatting from ChatGPT and Claude Output</title>
    <link href="https://www.markdowntools.com/blog/remove-markdown-formatting-chatgpt-claude-output" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/remove-markdown-formatting-chatgpt-claude-output</id>
    <published>2026-10-02T00:00:00+00:00</published>
    <updated>2026-10-02T00:00:00+00:00</updated>
    <author><name>Matthew Rathbone</name></author>
    <category term="Troubleshooting"/>
    <summary>AI chat answers arrive full of asterisks, pound signs and link syntax. Why it happens, three ways to get clean text out (and which one fits where you're pasting), and what plain text can't preserve.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/remove-markdown-formatting-chatgpt-claude-output">&lt;p&gt;You ask an AI assistant for a summary, paste it into an email or a form field, and the result is littered with &lt;code&gt;**&lt;/code&gt; pairs, &lt;code&gt;##&lt;/code&gt; prefixes and &lt;code&gt;[text](https://...)&lt;/code&gt; links. Nothing is broken. The assistant wrote Markdown, the chat window rendered it nicely, and your destination simply doesn&amp;#39;t speak Markdown.&lt;/p&gt;
&lt;p&gt;This post covers why that happens, three ways to get clean text out, and the cases where you should keep some formatting instead of stripping everything.&lt;/p&gt;
&lt;h2 id=&quot;why-ai-output-is-full-of-markdown&quot;&gt;Why AI output is full of Markdown&lt;/h2&gt;
&lt;p&gt;Chat assistants are trained and prompted to format answers with Markdown, because the chat interface renders it: bold for emphasis, &lt;code&gt;#&lt;/code&gt; for headings, &lt;code&gt;-&lt;/code&gt; for lists, backticks for code. What you see in the window is the &lt;em&gt;rendered&lt;/em&gt; result. What you often get on your clipboard depends on how you copy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A copy button on the message&lt;/strong&gt; typically copies the underlying Markdown source, asterisks and all. This is exactly what you want when pasting into GitHub or Notion, and exactly what you don&amp;#39;t want in an email.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Selecting rendered text with the mouse&lt;/strong&gt; usually copies rich text (real bold, real bullets). Destinations that accept rich text, such as Google Docs, Word and most email composers, keep the formatting. Destinations that only accept plain text drop the styling and keep whatever characters came along.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Which one you get varies by product and changes over time, so don&amp;#39;t rely on either. The reliable fix is to decide what you want the &lt;em&gt;destination&lt;/em&gt; to receive and convert on purpose.&lt;/p&gt;
&lt;h2 id=&quot;option-1-ask-for-plain-text-up-front&quot;&gt;Option 1: Ask for plain text up front&lt;/h2&gt;
&lt;p&gt;The cheapest fix is a one-line instruction: &amp;quot;Reply in plain text only, no Markdown formatting.&amp;quot; Most assistants follow it, and it works well for short answers.&lt;/p&gt;
&lt;p&gt;It&amp;#39;s less dependable when you&amp;#39;ve already got a long answer you like, when the model drifts back to Markdown several messages into a conversation, or when you&amp;#39;re stripping a document you didn&amp;#39;t generate yourself (a README, exported notes, a wiki page). It also costs you a re-run for every answer you&amp;#39;ve already received.&lt;/p&gt;
&lt;h2 id=&quot;option-2-strip-it-with-a-converter&quot;&gt;Option 2: Strip it with a converter&lt;/h2&gt;
&lt;p&gt;Pasting the Markdown into a converter and copying the clean result is the dependable approach, and it works on any Markdown, not just AI output. Our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-plain-text&quot;&gt;Markdown to Plain Text tool&lt;/a&gt; runs entirely in your browser and parses the text as real Markdown instead of deleting characters blindly, which matters in a few cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Nested formatting.&lt;/strong&gt; &lt;code&gt;**bold with _italic_ inside**&lt;/code&gt; should become the words, not a half-stripped mess.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lists.&lt;/strong&gt; Bullets become &lt;code&gt;•&lt;/code&gt; characters (or are removed, if you prefer), numbered items keep their numbers, and nested items stay indented.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Links.&lt;/strong&gt; By default you get just the link text. Tick &amp;quot;Keep link URLs&amp;quot; and each link becomes &lt;code&gt;text (url)&lt;/code&gt;, which is usually what you want in an email where the reader can&amp;#39;t click.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tables.&lt;/strong&gt; Markdown tables can&amp;#39;t survive in plain text, so each row becomes a line with tab-separated cells. That pastes straight into a spreadsheet.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code.&lt;/strong&gt; Fences and backticks go away; the code text stays (or you can drop code blocks entirely).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;option-3-do-it-yourself-with-a-regex-and-why-it-breaks&quot;&gt;Option 3: Do it yourself with a regex (and why it breaks)&lt;/h2&gt;
&lt;p&gt;A common shortcut is a handful of search-and-replace patterns:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-text&quot;&gt;**text**  -&amp;gt;  text
# Title   -&amp;gt;  Title
[a](b)    -&amp;gt;  a
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That handles the easy 80%, then fails in predictable ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A literal asterisk in prose (&lt;code&gt;5 * 3&lt;/code&gt;, a footnote star) gets eaten as if it were emphasis.&lt;/li&gt;
&lt;li&gt;Underscores inside identifiers (&lt;code&gt;snake_case_name&lt;/code&gt;) get mistaken for italics.&lt;/li&gt;
&lt;li&gt;Nested emphasis and links inside list items need ordering tricks that are easy to get wrong.&lt;/li&gt;
&lt;li&gt;Code blocks should be exempt from every rule, but a naive pass strips inside them too.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you only strip occasionally, a parser-based tool saves the debugging. If you&amp;#39;re scripting it, use a real Markdown parser and walk its output rather than layering patterns.&lt;/p&gt;
&lt;h2 id=&quot;when-you-should-not-strip-everything&quot;&gt;When you should &lt;em&gt;not&lt;/em&gt; strip everything&lt;/h2&gt;
&lt;p&gt;Plain text deletes meaning along with the markup. Before stripping, check whether the destination actually has a better option:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;th&gt;Better approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Slack&lt;/td&gt;
&lt;td&gt;Convert to Slack&amp;#39;s own syntax with the &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-slack&quot;&gt;Markdown to Slack tool&lt;/a&gt;. Bold and links survive. See our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-slack-formatting-guide&quot;&gt;Slack formatting guide&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Word or Google Docs&lt;/td&gt;
&lt;td&gt;Convert to a real document with &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-docx&quot;&gt;Markdown to Word&lt;/a&gt;, keeping headings and tables.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email newsletter&lt;/td&gt;
&lt;td&gt;Produce inline-styled HTML with &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-email-html&quot;&gt;Markdown to Email HTML&lt;/a&gt;. See the &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-email-html-newsletters-and-esps&quot;&gt;email HTML guide&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notion, Jira, Confluence&lt;/td&gt;
&lt;td&gt;These have their own import paths; see &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-for-notion-what-actually-works&quot;&gt;Notion&lt;/a&gt; and &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-jira-wiki-markup-guide&quot;&gt;Jira&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plain-text field, SMS, form, CMS that shows raw text&lt;/td&gt;
&lt;td&gt;Strip it. This is the right case for the plain text tool.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;what-gets-lost&quot;&gt;What gets lost&lt;/h2&gt;
&lt;p&gt;Be aware of what plain text cannot carry: emphasis (bold and italic are gone by definition), heading hierarchy (a heading becomes an ordinary line, so add your own spacing or capitalization if the structure matters), and anything that needed rendering, such as Mermaid diagrams, math and footnotes. Those pass through as their source text. If your content leans on them, see how &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-footnotes-across-platforms&quot;&gt;footnotes&lt;/a&gt; and &lt;a href=&quot;https://www.markdowntools.com/blog/mermaid-diagrams-in-slack-jira-confluence-word-email&quot;&gt;diagrams&lt;/a&gt; behave across platforms.&lt;/p&gt;
&lt;h2 id=&quot;quick-checklist&quot;&gt;Quick checklist&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Know where the text is going. Rich-text target: copy rendered text. Markdown-aware target: paste the source. Plain-text-only target: strip.&lt;/li&gt;
&lt;li&gt;For one-off answers, ask the assistant for plain text first.&lt;/li&gt;
&lt;li&gt;For everything else, run it through a parser-based converter and decide on link URLs and bullets.&lt;/li&gt;
&lt;li&gt;Skim the output once. Stripped lists and tables are the places most likely to need a manual touch.&lt;/li&gt;
&lt;/ol&gt;
</content>
  </entry>
  <entry>
    <title>Markdown Footnotes Across Platforms: Where They Actually Render</title>
    <link href="https://www.markdowntools.com/blog/markdown-footnotes-across-platforms" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/markdown-footnotes-across-platforms</id>
    <published>2026-09-25T00:00:00+00:00</published>
    <updated>2026-09-25T00:00:00+00:00</updated>
    <author><name>Matthew Rathbone</name></author>
    <category term="Reference"/>
    <summary>GitHub, GitLab, Obsidian, and this very blog render [^1] footnotes natively — Notion, Slack, Jira, and Confluence don't support them at all. A platform-by-platform reference for where footnote syntax actually works.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/markdown-footnotes-across-platforms">&lt;p&gt;Footnote syntax — a reference marker like &lt;code&gt;[^1]&lt;/code&gt; in the body text, paired with a definition &lt;code&gt;[^1]: The actual note.&lt;/code&gt; elsewhere in the document — looks the same everywhere you write it. Whether it &lt;em&gt;renders&lt;/em&gt; as anything at all is a different question entirely, and the answer splits sharply by platform: some render real, clickable, jump-linked footnotes with zero configuration; others don&amp;#39;t understand the syntax at all and will show you the literal brackets and carets. This is a reference for which is which, checked against real output rather than assumed from a spec.&lt;/p&gt;
&lt;p&gt;If you haven&amp;#39;t used footnote syntax before, our &lt;a href=&quot;https://www.markdowntools.com/blog/footnotes-in-markdown&quot;&gt;footnotes in Markdown guide&lt;/a&gt; covers the basic syntax first. If you&amp;#39;re doing heavier academic-style citation work, see our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-citations-and-references-guide&quot;&gt;citations and references guide&lt;/a&gt;. This post is specifically about the one thing those guides don&amp;#39;t fully settle: does your renderer actually turn &lt;code&gt;[^1]&lt;/code&gt; into a footnote, or not.&lt;/p&gt;
&lt;h2 id=&quot;native-support-no-configuration-needed&quot;&gt;Native support, no configuration needed&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt; added real footnote rendering in September 2021 — this is worth calling out explicitly, because plenty of older Markdown content (including, until this post, &lt;a href=&quot;https://www.markdowntools.com/blog/footnotes-in-markdown&quot;&gt;our own footnotes guide&lt;/a&gt;) still says GitHub doesn&amp;#39;t support them, which was true before that date and isn&amp;#39;t anymore. Today, &lt;code&gt;[^1]&lt;/code&gt; in an issue, pull request, wiki page, or repository file renders as a real superscript link that jumps to a numbered footnotes section at the bottom, with a back-link to return to your place. It works the same way in Markdown files rendered on GitHub Pages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GitLab&lt;/strong&gt; renders footnotes the same way, and has for longer than GitHub — GitLab Flavored Markdown has included footnote support across issues, merge requests, and wiki pages for several major versions now.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Obsidian&lt;/strong&gt; supports the same reference-and-definition syntax, plus its own shorthand: &lt;code&gt;^[an inline footnote written right here]&lt;/code&gt; creates a footnote without a separate definition elsewhere in the document at all. Both forms render as a small superscript number that shows the note content in a popover on hover or click, without navigating away from your place in the document.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This blog&lt;/strong&gt; — and any Jekyll site using kramdown, which is Jekyll&amp;#39;s default Markdown processor — renders footnotes natively too. We checked this directly against kramdown&amp;#39;s actual output rather than assuming it from the spec: &lt;code&gt;[^1]&lt;/code&gt; produces a real &lt;code&gt;&amp;lt;sup&amp;gt;&lt;/code&gt;-wrapped jump link, and the definition becomes a proper &lt;code&gt;&amp;lt;ol&amp;gt;&lt;/code&gt; entry with a reverse-footnote back-link, identical in shape to GitHub&amp;#39;s rendering. No plugin, no configuration flag — kramdown just handles it, which is presumably part of why footnote syntax proliferated on GitHub-adjacent sites in the first place.&lt;/p&gt;
&lt;h2 id=&quot;needs-configuration--off-by-default&quot;&gt;Needs configuration — off by default&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;MkDocs&lt;/strong&gt;, using the standard Python-Markdown processor, does &lt;em&gt;not&lt;/em&gt; render footnotes out of the box. You need to explicitly enable the &lt;code&gt;footnotes&lt;/code&gt; extension in &lt;code&gt;mkdocs.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;markdown_extensions:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;footnotes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Without it, &lt;code&gt;[^1]&lt;/code&gt; and &lt;code&gt;[^1]: text&lt;/code&gt; just print as literal characters — there&amp;#39;s no error, no warning, just plain text where you expected a footnote. If a MkDocs site you&amp;#39;re working on shows raw footnote syntax instead of rendered notes, this is almost always why.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Docusaurus&lt;/strong&gt;, built on MDX, needs a comparable opt-in. Recent Docusaurus versions ship with GFM support (via &lt;code&gt;remark-gfm&lt;/code&gt;) enabled in the default preset, which includes footnote parsing — but if you&amp;#39;re on an older config, a custom MDX pipeline, or you&amp;#39;ve deliberately swapped remark plugins, footnotes can silently stop working the same way they would on MkDocs without its extension. Worth a quick test render before you rely on them in a Docusaurus-based doc site, the same advice this blog&amp;#39;s &lt;a href=&quot;https://www.markdowntools.com/blog/mkdocs-vs-docusaurus-vs-gitbook&quot;&gt;MkDocs vs. Docusaurus vs. GitBook guide&lt;/a&gt; already gives for other GFM-adjacent features.&lt;/p&gt;
&lt;h2 id=&quot;no-support-at-all&quot;&gt;No support at all&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Notion&lt;/strong&gt; doesn&amp;#39;t recognize footnote syntax on import or paste — &lt;code&gt;[^1]&lt;/code&gt; and its definition just come through as literal text sitting wherever they land, with no connection drawn between them. Our own &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-for-notion-what-actually-works&quot;&gt;Notion companion guide&lt;/a&gt; covers this as one of the format&amp;#39;s known casualties, and it&amp;#39;s also why the &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-notion&quot;&gt;Markdown to Notion tool&lt;/a&gt; takes the deliberate step of inlining footnote text next to its reference before conversion — Notion never runs a footnotes step of its own, so if nobody flattens them first, they&amp;#39;re just lost.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Slack&lt;/strong&gt; has no footnote handling anywhere in its formatting — not in the message composer, not via mrkdwn sent through the API, not in Block Kit. Same story in &lt;strong&gt;Jira&lt;/strong&gt; and &lt;strong&gt;Confluence&lt;/strong&gt; wiki markup: no equivalent syntax exists in either, native or otherwise. The practical workaround in all three, and what each platform&amp;#39;s companion guide on this blog recommends, is a plain parenthetical aside inline instead of a real footnote — there&amp;#39;s no rendering feature to fall back on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Microsoft Word&lt;/strong&gt; and &lt;strong&gt;HTML email&lt;/strong&gt; don&amp;#39;t fare any better once you&amp;#39;re converting &lt;em&gt;into&lt;/em&gt; them from Markdown, for a related but distinct reason: it&amp;#39;s not that Word or email clients can&amp;#39;t display footnotes (Word obviously has real native footnote support) — it&amp;#39;s that the client-side Markdown parsers doing the conversion (used by tools like our own &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-docx&quot;&gt;Markdown to Word&lt;/a&gt; and &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-email-html&quot;&gt;Markdown to Email HTML&lt;/a&gt; converters) don&amp;#39;t implement the footnote extension, so &lt;code&gt;[^1]&lt;/code&gt; passes through as literal bracket-and-caret text in the output rather than becoming a real Word footnote or an email-safe substitute.&lt;/p&gt;
&lt;h2 id=&quot;quick-reference&quot;&gt;Quick reference&lt;/h2&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Footnotes render?&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;GitHub&lt;/td&gt;
&lt;td&gt;Yes, natively&lt;/td&gt;
&lt;td&gt;Since September 2021&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitLab&lt;/td&gt;
&lt;td&gt;Yes, natively&lt;/td&gt;
&lt;td&gt;Longer-standing support than GitHub&amp;#39;s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Obsidian&lt;/td&gt;
&lt;td&gt;Yes, natively&lt;/td&gt;
&lt;td&gt;Also supports inline &lt;code&gt;^[...]&lt;/code&gt; shorthand&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jekyll (kramdown)&lt;/td&gt;
&lt;td&gt;Yes, natively&lt;/td&gt;
&lt;td&gt;Verified directly against kramdown&amp;#39;s own output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MkDocs&lt;/td&gt;
&lt;td&gt;Needs config&lt;/td&gt;
&lt;td&gt;Enable the &lt;code&gt;footnotes&lt;/code&gt; extension in &lt;code&gt;mkdocs.yml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docusaurus&lt;/td&gt;
&lt;td&gt;Usually, via GFM&lt;/td&gt;
&lt;td&gt;Confirm your remark/MDX config includes it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notion&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Text passes through unlinked; import tools inline it instead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slack&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No equivalent in mrkdwn or Block Kit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jira / Confluence&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No wiki-markup equivalent in either&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Word / Email HTML&lt;/td&gt;
&lt;td&gt;No (via conversion)&lt;/td&gt;
&lt;td&gt;The converting parser doesn&amp;#39;t expand the syntax&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;cleaning-up-messy-footnotes-before-you-publish&quot;&gt;Cleaning up messy footnotes before you publish&lt;/h2&gt;
&lt;p&gt;None of the above matters much if your footnote numbering has drifted — which happens the moment you add, remove, or reorder a footnote in an existing document, since every reference and definition after that point needs updating in lockstep by hand. Our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-footnotes&quot;&gt;Footnote Organizer&lt;/a&gt; renumbers every footnote sequentially by the order it&amp;#39;s first referenced (not the order its definition happens to be written), collects all the definitions at the document&amp;#39;s end, and flags anything it can&amp;#39;t safely fix on its own — a reference with no matching definition, or a definition nobody ever referenced — rather than silently guessing.&lt;/p&gt;
&lt;h2 id=&quot;related-reading&quot;&gt;Related reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/footnotes-in-markdown&quot;&gt;How to Use Footnotes in Markdown&lt;/a&gt; — the basic syntax&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-citations-and-references-guide&quot;&gt;Markdown Citations and References&lt;/a&gt; — academic citation styles and bibliography tools&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-platform-compatibility-guide&quot;&gt;Markdown Platform Compatibility Guide&lt;/a&gt; — the broader picture beyond just footnotes&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/why-your-markdown-isnt-rendering&quot;&gt;Why Your Markdown Isn&amp;#39;t Rendering&lt;/a&gt; — troubleshooting other silent syntax failures&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Markdown Comments Across Platforms: Hidden, Stripped, or Still Sitting in Your HTML?</title>
    <link href="https://www.markdowntools.com/blog/markdown-comments-across-platforms" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/markdown-comments-across-platforms</id>
    <published>2026-09-18T00:00:00+00:00</published>
    <updated>2026-09-18T00:00:00+00:00</updated>
    <author><name>Matthew Rathbone</name></author>
    <category term="Tutorial"/>
    <summary>HTML comments in Markdown don't behave the same way everywhere. See which platforms actually remove them, which just hide them from rendering, and why that distinction matters for anything sensitive.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/markdown-comments-across-platforms">&lt;p&gt;If you&amp;#39;ve read our &lt;a href=&quot;https://www.markdowntools.com/blog/how-to-add-comments-in-markdown&quot;&gt;guide to adding comments in Markdown&lt;/a&gt;, you already know the syntax: wrap text in &lt;code&gt;&amp;lt;!-- --&amp;gt;&lt;/code&gt; and it won&amp;#39;t show up in the rendered output. That part is simple and it&amp;#39;s the same everywhere.&lt;/p&gt;
&lt;p&gt;What isn&amp;#39;t the same everywhere is what &amp;quot;won&amp;#39;t show up&amp;quot; actually means. On some platforms, an HTML comment is truly deleted before the page is ever generated — it never exists in the shipped output at all. On others, it&amp;#39;s just told not to &lt;em&gt;display&lt;/em&gt;, while the literal text sits right there in the page&amp;#39;s HTML source, visible to anyone who right-clicks and chooses &amp;quot;View Page Source.&amp;quot; And on a few platforms, the comment doesn&amp;#39;t get hidden at all — it gets stripped so aggressively that the whole thing, syntax and content, just vanishes on import.&lt;/p&gt;
&lt;p&gt;Those are three genuinely different outcomes, and mixing them up is how internal notes end up leaking, or how a &amp;quot;hidden&amp;quot; TODO turns into a paragraph of literal &lt;code&gt;&amp;lt;!--&lt;/code&gt; characters on a live page. This guide walks through what actually happens, platform by platform.&lt;/p&gt;
&lt;h2 id=&quot;the-distinction-that-matters-removed-vs-hidden&quot;&gt;The distinction that matters: removed vs. hidden&lt;/h2&gt;
&lt;p&gt;Before the platform tour, it&amp;#39;s worth naming the core difference explicitly, because it&amp;#39;s the thing most comment tutorials skip.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Removed at build time&lt;/strong&gt; means the comment&amp;#39;s content never makes it into the file that gets served to a browser. If you view the page source, it&amp;#39;s simply not there. This is what you want for anything you&amp;#39;d be embarrassed to have a curious visitor stumble across — draft pricing, internal reviewer names, &amp;quot;don&amp;#39;t publish this until legal signs off,&amp;quot; that kind of thing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hidden at render time&lt;/strong&gt; means the comment&amp;#39;s content &lt;em&gt;is&lt;/em&gt; in the HTML that gets served — it&amp;#39;s just wrapped in &lt;code&gt;&amp;lt;!-- --&amp;gt;&lt;/code&gt;, which browsers know not to display. A regular visitor won&amp;#39;t see it in the rendered page. Anyone who opens dev tools or view-source will see it immediately, in plain text.&lt;/p&gt;
&lt;p&gt;Standard HTML comments (&lt;code&gt;&amp;lt;!-- like this --&amp;gt;&lt;/code&gt;) are almost always the second kind, not the first. That&amp;#39;s true no matter what Markdown processor you&amp;#39;re using, because Markdown itself doesn&amp;#39;t do anything special with HTML comments — it passes them through untouched, and it&amp;#39;s the browser, not Markdown, that decides not to render them visually. The confusion comes from platforms that layer a &lt;em&gt;build system&lt;/em&gt; on top of Markdown, because some of those build systems have their own, separate comment syntax that behaves differently. More on that below.&lt;/p&gt;
&lt;h2 id=&quot;platform-by-platform-behavior&quot;&gt;Platform-by-platform behavior&lt;/h2&gt;
&lt;h3 id=&quot;github--repo-files-vs-issuesprs&quot;&gt;GitHub — repo files vs. Issues/PRs&lt;/h3&gt;
&lt;p&gt;In a rendered file (a README, a wiki page, anything under a repo&amp;#39;s Markdown rendering), HTML comments are hidden-not-removed: view the raw file (&lt;code&gt;?raw=true&lt;/code&gt; or the &amp;quot;Raw&amp;quot; button) and the comment text is right there. This is expected and fine for a public repo, since anyone can already read your whole file&amp;#39;s raw source anyway — there&amp;#39;s no secret being kept from anyone.&lt;/p&gt;
&lt;p&gt;Issues and pull requests behave the same way but get used differently in practice: GitHub&amp;#39;s own issue and PR templates use HTML comments as instructional placeholder text (&lt;code&gt;&amp;lt;!-- Please describe the change and link any related issues --&amp;gt;&lt;/code&gt;). Once submitted, that instructional text is hidden in the rendered comment, exactly as designed — but it&amp;#39;s still sitting in the issue body&amp;#39;s raw Markdown, retrievable via the API or an &amp;quot;Edit&amp;quot; click. Don&amp;#39;t put anything in a PR template comment you wouldn&amp;#39;t want a bot or a scraper reading back to you later.&lt;/p&gt;
&lt;h3 id=&quot;gitlab&quot;&gt;GitLab&lt;/h3&gt;
&lt;p&gt;Functionally identical to GitHub for both files and merge request descriptions — HTML comments hide in rendered view, remain fully present in the raw Markdown. GitLab also uses the same instructional-placeholder-comment pattern in its default MR templates.&lt;/p&gt;
&lt;h3 id=&quot;npm-and-pypi-package-pages&quot;&gt;npm and PyPI package pages&lt;/h3&gt;
&lt;p&gt;Both npmjs.com and pypi.org render your &lt;code&gt;README.md&lt;/code&gt; for the package listing page using largely the same rendering assumptions as GitHub. HTML comments are hidden from the rendered listing but present in the page&amp;#39;s HTML source — worth remembering if your README&amp;#39;s HTML comments contain anything like internal build notes or unpublished feature flags.&lt;/p&gt;
&lt;h3 id=&quot;jekyll-this-sites-own-engine--and-why-its-a-special-case&quot;&gt;Jekyll (this site&amp;#39;s own engine) — and why it&amp;#39;s a special case&lt;/h3&gt;
&lt;p&gt;This is where the removed-vs-hidden distinction stops being theoretical, because Jekyll gives you &lt;em&gt;both&lt;/em&gt; options, and they&amp;#39;re not interchangeable.&lt;/p&gt;
&lt;p&gt;A plain HTML comment in a Jekyll post behaves like everywhere else — hidden from the rendered page, present in the shipped HTML:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&amp;lt;!-- This survives the build and sits in the final HTML, just invisible --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But Jekyll&amp;#39;s Liquid templating layer also has its own comment tag, and it works completely differently:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-liquid&quot;&gt;{% comment %}
This text is deleted during the build. It never appears
anywhere in the generated _site output, not even in the source.
{% endcomment %}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;code&gt;{% comment %}...{% endcomment %}&lt;/code&gt; content is stripped by Jekyll itself, before the HTML is ever written to disk. That&amp;#39;s the actual &amp;quot;removed at build time&amp;quot; behavior — genuinely useful for author-only notes you don&amp;#39;t want in your Git-hosted output at all, as opposed to notes you&amp;#39;re fine with a curious reader finding in view-source.&lt;/p&gt;
&lt;p&gt;The catch: &lt;code&gt;{% comment %}&lt;/code&gt; only works in files Jekyll actually processes through Liquid (posts and pages with front matter). It does nothing useful inside a fenced code block meant to &lt;em&gt;display&lt;/em&gt; Liquid syntax as an example — in fact, unescaped &lt;code&gt;{% %}&lt;/code&gt; or &lt;code&gt;{{ }}&lt;/code&gt; inside a code sample is exactly the class of bug we&amp;#39;ve written about &lt;a href=&quot;https://www.markdowntools.com/blog/why-your-markdown-isnt-rendering&quot;&gt;in our own troubleshooting guide&lt;/a&gt;, where Jekyll tries to execute what you meant as illustrative text.&lt;/p&gt;
&lt;h3 id=&quot;hugo&quot;&gt;Hugo&lt;/h3&gt;
&lt;p&gt;Hugo has the same two-tier structure as Jekyll, just with different names. Plain HTML comments behave like everywhere else: hidden, not removed. Hugo templates additionally support Go template comments (&lt;code&gt;{{/* like this */}}&lt;/code&gt;) for &lt;code&gt;.html&lt;/code&gt; layout files, which are stripped at build time — but that syntax is a layout-file feature, not something you write inside your Markdown content itself. If you&amp;#39;re commenting inside a Markdown post body on a Hugo site, you&amp;#39;re almost always using the plain HTML form, which means: hidden, not removed.&lt;/p&gt;
&lt;h3 id=&quot;mkdocs-docusaurus-and-other-static-site-generators&quot;&gt;MkDocs, Docusaurus, and other static-site generators&lt;/h3&gt;
&lt;p&gt;These follow the same general rule as Jekyll and Hugo&amp;#39;s content layer: they pass HTML comments through to the rendered HTML untouched. The comment is hidden visually but present in source. Docusaurus (React/MDX-based) is worth a specific flag: because MDX treats &lt;code&gt;{&lt;/code&gt; as the start of a JavaScript expression, curly-brace syntax in prose can trigger build errors in ways plain Jekyll/Hugo content doesn&amp;#39;t — a separate gotcha from the comment-visibility question, but one that trips up the same audience.&lt;/p&gt;
&lt;h3 id=&quot;confluence&quot;&gt;Confluence&lt;/h3&gt;
&lt;p&gt;Confluence doesn&amp;#39;t render Markdown to HTML the way the platforms above do — it converts to its own storage format, and as &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-in-confluence-what-works&quot;&gt;our Confluence guide&lt;/a&gt; documents, raw HTML gets stripped or escaped on import rather than preserved. In practice, that means an HTML comment pasted into Confluence&amp;#39;s &lt;code&gt;/markdown&lt;/code&gt; panel usually doesn&amp;#39;t survive as a hidden comment at all — it&amp;#39;s either dropped entirely or shows up as literal, visible &lt;code&gt;&amp;lt;!--&lt;/code&gt; text, depending on the import path. If you need a genuinely hidden internal note in Confluence, use a real Confluence feature built for it (an inline comment, or the &lt;code&gt;{noformat}&lt;/code&gt;/expand macros) rather than relying on Markdown syntax that the platform was never built to honor.&lt;/p&gt;
&lt;h3 id=&quot;notion&quot;&gt;Notion&lt;/h3&gt;
&lt;p&gt;Notion strips raw HTML entirely on import, and that includes comment syntax — it&amp;#39;s not a &amp;quot;hidden but present&amp;quot; situation, it&amp;#39;s a &amp;quot;gone&amp;quot; situation, per &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-for-notion-what-actually-works&quot;&gt;our Notion compatibility guide&lt;/a&gt;. If you need a genuinely invisible internal note inside a Notion page, Notion&amp;#39;s own commenting feature (select text, add a comment) is the tool for that job, not an HTML comment carried over from a Markdown file.&lt;/p&gt;
&lt;h3 id=&quot;obsidian&quot;&gt;Obsidian&lt;/h3&gt;
&lt;p&gt;Obsidian is the one platform on this list where &amp;quot;hidden vs. removed&amp;quot; barely applies, because there&amp;#39;s no build/publish step by default — you&amp;#39;re just looking at your own local file. In Reading view, an HTML comment is hidden the same way it would be in a browser. In Source/Live Preview mode, you see it as plain text, since you&amp;#39;re literally looking at the file. Nothing is ever removed; it&amp;#39;s your file, sitting on your disk, exactly as you typed it, until you delete it yourself.&lt;/p&gt;
&lt;h3 id=&quot;vs-code-markdown-preview&quot;&gt;VS Code Markdown Preview&lt;/h3&gt;
&lt;p&gt;The simplest case on this list: the built-in preview pane hides HTML comments, the editor pane shows them, because you&amp;#39;re editing a plain local file and the preview is just a live render of it — no build step, no publish step, nothing gets removed.&lt;/p&gt;
&lt;h2 id=&quot;quick-reference&quot;&gt;Quick reference&lt;/h2&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform / context&lt;/th&gt;
&lt;th&gt;HTML comment behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;GitHub (files, Issues, PRs)&lt;/td&gt;
&lt;td&gt;Hidden in render, present in raw source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitLab (files, MRs)&lt;/td&gt;
&lt;td&gt;Hidden in render, present in raw source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;npm / PyPI README&lt;/td&gt;
&lt;td&gt;Hidden in render, present in page source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jekyll — &lt;code&gt;&amp;lt;!-- --&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hidden in render, present in shipped HTML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jekyll — &lt;code&gt;{% comment %}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removed entirely at build time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hugo — &lt;code&gt;&amp;lt;!-- --&amp;gt;&lt;/code&gt; in content&lt;/td&gt;
&lt;td&gt;Hidden in render, present in shipped HTML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hugo — &lt;code&gt;{{/* */}}&lt;/code&gt; in layouts&lt;/td&gt;
&lt;td&gt;Removed entirely at build time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MkDocs / Docusaurus&lt;/td&gt;
&lt;td&gt;Hidden in render, present in shipped HTML&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confluence&lt;/td&gt;
&lt;td&gt;Usually dropped or shown literally — not reliably hidden&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notion&lt;/td&gt;
&lt;td&gt;Stripped entirely on import&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Obsidian&lt;/td&gt;
&lt;td&gt;Nothing removed — visible in Source mode, hidden in Reading view&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VS Code preview&lt;/td&gt;
&lt;td&gt;Hidden in preview pane, visible in editor pane&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;what-this-means-in-practice&quot;&gt;What this means in practice&lt;/h2&gt;
&lt;p&gt;Two practical takeaways follow directly from the table above.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Never treat an HTML comment as a privacy or security boundary.&lt;/strong&gt; On the majority of platforms here, &amp;quot;hidden&amp;quot; means &amp;quot;not displayed,&amp;quot; not &amp;quot;not present.&amp;quot; Anything sensitive — API keys, internal pricing, a note about a security issue you haven&amp;#39;t disclosed yet — needs to actually not be in the file at all, or needs to live somewhere with real access control. If you&amp;#39;re publishing through Jekyll or Hugo specifically and want a note that genuinely never reaches the output, use the templating engine&amp;#39;s own comment tag (&lt;code&gt;{% comment %}&lt;/code&gt; or &lt;code&gt;{{/* */}}&lt;/code&gt;), not a plain HTML comment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don&amp;#39;t assume a comment will survive a platform migration.&lt;/strong&gt; If you&amp;#39;re moving content from a Git-based Markdown workflow into Confluence or Notion, any HTML comments you were relying on for editorial notes, TODOs, or draft flags will very likely not make the trip — either they&amp;#39;ll vanish silently or, worse, show up as visible clutter. Strip them out (or convert them to that platform&amp;#39;s native commenting feature) before you migrate, rather than after someone notices.&lt;/p&gt;
&lt;p&gt;You can test any of this yourself before committing to a workflow: paste Markdown containing comments into the &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-editor&quot;&gt;Markdown Live Preview Editor&lt;/a&gt; and check both the rendered pane and the raw output. It won&amp;#39;t simulate a Confluence or Notion import, but it will immediately confirm the base case — hidden in render, present in source — that every platform above starts from before adding its own quirks on top.&lt;/p&gt;
&lt;h2 id=&quot;related-reading&quot;&gt;Related reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/how-to-add-comments-in-markdown&quot;&gt;How to Add Comments in Markdown&lt;/a&gt; — the syntax fundamentals this guide builds on&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/why-your-markdown-isnt-rendering&quot;&gt;Why Your Markdown Isn&amp;#39;t Rendering&lt;/a&gt; — troubleshooting for when template syntax gets executed instead of displayed&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-in-confluence-what-works&quot;&gt;Markdown in Confluence: What Actually Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-for-notion-what-actually-works&quot;&gt;Markdown for Notion: What Actually Works and What Doesn&amp;#39;t&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-platform-compatibility-guide&quot;&gt;Markdown Across Platforms: The Compatibility Guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Mermaid Diagrams in Slack, Jira, Confluence, Word, and Email: The Static Export Workaround</title>
    <link href="https://www.markdowntools.com/blog/mermaid-diagrams-in-slack-jira-confluence-word-email" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/mermaid-diagrams-in-slack-jira-confluence-word-email</id>
    <published>2026-09-18T00:00:00+00:00</published>
    <updated>2026-09-18T00:00:00+00:00</updated>
    <author><name>Matthew Rathbone</name></author>
    <category term="Tutorial"/>
    <summary>Mermaid renders live on GitHub, GitLab, and Obsidian — and nowhere else most teams actually work. Here's how to get a Mermaid diagram into Slack, Jira, Confluence, Word, and email as a static image instead.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/mermaid-diagrams-in-slack-jira-confluence-word-email">&lt;p&gt;Our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-diagrams-flowcharts-complete-guide&quot;&gt;complete guide to diagrams and flowcharts in Markdown&lt;/a&gt; covers Mermaid syntax in depth — flowcharts, sequence diagrams, ER diagrams, the works. What it doesn&amp;#39;t dwell on is a problem that shows up the moment you try to actually &lt;em&gt;use&lt;/em&gt; one of those diagrams somewhere other than a GitHub README: most of the places you&amp;#39;d want to paste a diagram don&amp;#39;t render Mermaid at all.&lt;/p&gt;
&lt;p&gt;That&amp;#39;s not a syntax problem, and no amount of getting the Mermaid code &amp;quot;right&amp;quot; fixes it. It&amp;#39;s a rendering-engine problem — Slack, Jira, Confluence, Word, and email clients simply have no Mermaid interpreter anywhere in their pipeline. The fix isn&amp;#39;t better Markdown. It&amp;#39;s exporting the diagram as a plain image before it leaves your Mermaid source.&lt;/p&gt;
&lt;h2 id=&quot;why-this-keeps-catching-people-off-guard&quot;&gt;Why this keeps catching people off guard&lt;/h2&gt;
&lt;p&gt;Mermaid fences (&lt;code&gt;```mermaid&lt;/code&gt;) look exactly like any other fenced code block, and on the platforms that *do* support it — GitHub, GitLab, Obsidian — that fence quietly becomes a rendered diagram with zero extra steps. It&amp;#39;s easy to assume the same thing will happen everywhere else, because nothing about the syntax signals &amp;quot;this only works in three places.&amp;quot; It doesn&amp;#39;t error. It doesn&amp;#39;t warn you. It just shows up as a gray code block of &lt;code&gt;flowchart TD&lt;/code&gt; and arrow syntax, exactly as literally typed, and the reader has to reconstruct the diagram in their head from the text.&lt;/p&gt;
&lt;p&gt;We ran into this from the other direction first, while documenting our own conversion tools: our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-for-notion-what-actually-works&quot;&gt;Notion conversion guide&lt;/a&gt;, &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-word-docx-what-converts-cleanly&quot;&gt;Word/DOCX guide&lt;/a&gt;, and &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-email-html-newsletters-and-esps&quot;&gt;Email HTML guide&lt;/a&gt; all independently document the same fallback: a Mermaid fence gets carried through as an inert code block, with a warning, because there&amp;#39;s no other honest thing a converter can do with syntax the destination format can&amp;#39;t execute. Slack and Jira have the identical gap for a different reason — there&amp;#39;s no conversion step at all, since you&amp;#39;re typically composing directly in Slack&amp;#39;s message box or Jira&amp;#39;s editor, and neither one has ever implemented a Mermaid renderer.&lt;/p&gt;
&lt;h2 id=&quot;the-fix-render-it-once-export-a-static-image&quot;&gt;The fix: render it once, export a static image&lt;/h2&gt;
&lt;p&gt;The workaround is the same regardless of destination: render the Mermaid source into an actual image — SVG or PNG — outside the target platform, then paste or attach &lt;em&gt;that&lt;/em&gt; instead of the Mermaid code. Once it&amp;#39;s a PNG or SVG, every platform on this list handles it exactly the same way it handles any other image, because at that point it &lt;em&gt;is&lt;/em&gt; just an image.&lt;/p&gt;
&lt;p&gt;You can do this with the Mermaid CLI (&lt;code&gt;mmdc&lt;/code&gt;) if you&amp;#39;re comfortable with a Node toolchain and want it scripted into a build step — our diagrams guide covers that command-line path. For a one-off diagram, or if you don&amp;#39;t want to install anything, our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-mermaid&quot;&gt;Markdown Mermaid Diagram Preview&lt;/a&gt; tool does the same rendering step in the browser: paste your Mermaid source, see it render live (so you catch syntax errors before you export, not after you&amp;#39;ve already pasted a broken image somewhere), then use &lt;strong&gt;Download SVG&lt;/strong&gt; or &lt;strong&gt;Download PNG&lt;/strong&gt; to get a static file with nothing installed.&lt;/p&gt;
&lt;h2 id=&quot;platform-by-platform&quot;&gt;Platform by platform&lt;/h2&gt;
&lt;h3 id=&quot;slack&quot;&gt;Slack&lt;/h3&gt;
&lt;p&gt;Slack has no diagram rendering of any kind — not in the message composer, not via the API, not through Block Kit. A Mermaid fence pasted into a Slack message just shows up as plain text with backticks, per our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-slack-formatting-guide&quot;&gt;Markdown to Slack guide&lt;/a&gt;, which documents the same &amp;quot;no native rendering, period&amp;quot; behavior for tables and several other Markdown constructs. Export a PNG and drag it into the message instead — Slack handles inline image previews natively, and unlike a wall of Mermaid syntax, a teammate can actually read it without decoding the diagram in their head.&lt;/p&gt;
&lt;h3 id=&quot;jira&quot;&gt;Jira&lt;/h3&gt;
&lt;p&gt;Neither Jira&amp;#39;s classic wiki markup nor its modern Cloud rich-text editor has a Mermaid renderer, as our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-jira-wiki-markup-guide&quot;&gt;Markdown to Jira guide&lt;/a&gt; notes for wiki-markup conversion generally. Some teams install a marketplace add-on (several exist specifically for embedding live Mermaid/PlantUML diagrams in Jira issues), which is worth it if diagrams are a constant part of your workflow — but for an occasional diagram in a ticket, attaching an exported PNG is faster than getting a marketplace app approved and installed.&lt;/p&gt;
&lt;h3 id=&quot;confluence&quot;&gt;Confluence&lt;/h3&gt;
&lt;p&gt;Confluence&amp;#39;s &lt;code&gt;/markdown&lt;/code&gt; paste panel strips or escapes raw HTML and doesn&amp;#39;t execute embedded scripts, so a Mermaid fence pasted in comes through as a plain code-formatted block, consistent with what our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-in-confluence-what-works&quot;&gt;Confluence compatibility guide&lt;/a&gt; documents for other unsupported constructs. As with Jira, marketplace add-ons exist for live Mermaid rendering in Confluence pages if you need diagrams to stay editable in place; for a document going out once, attach the exported image and move on.&lt;/p&gt;
&lt;h3 id=&quot;word-docx&quot;&gt;Word (.docx)&lt;/h3&gt;
&lt;p&gt;A &lt;code&gt;.docx&lt;/code&gt; file has no code-execution layer at all — there&amp;#39;s no such thing as a &amp;quot;live&amp;quot; Mermaid diagram inside a Word document, full stop. Our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-word-docx-what-converts-cleanly&quot;&gt;Markdown to Word guide&lt;/a&gt; documents that our own &lt;code&gt;markdown-to-docx&lt;/code&gt; converter drops Mermaid fences to a plain monospace code block with a warning, for exactly this reason — there&amp;#39;s nothing else a converter &lt;em&gt;could&lt;/em&gt; do with it. Export the diagram as a PNG first, then either paste it directly into the document or use our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-image-formatter&quot;&gt;Markdown Image Formatter&lt;/a&gt; if you want a sized/captioned version before it goes in.&lt;/p&gt;
&lt;h3 id=&quot;email-newsletters-transactional-templates&quot;&gt;Email (newsletters, transactional templates)&lt;/h3&gt;
&lt;p&gt;Same root problem as Word, for the same reason: email clients render HTML, not JavaScript, and Mermaid needs JavaScript to do its layout work. Our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-email-html-newsletters-and-esps&quot;&gt;Markdown to Email HTML guide&lt;/a&gt; documents the identical fallback — a Mermaid fence becomes a plain code block with a warning banner. For email specifically, export &lt;strong&gt;PNG&lt;/strong&gt;, not SVG: SVG support in email clients is inconsistent (particularly in Outlook&amp;#39;s desktop Word-based rendering engine, already a recurring theme in that guide), while a PNG is universally safe.&lt;/p&gt;
&lt;h2 id=&quot;svg-vs-png-which-to-export&quot;&gt;SVG vs. PNG: which to export&lt;/h2&gt;
&lt;p&gt;Reach for &lt;strong&gt;SVG&lt;/strong&gt; when the destination platform accepts vector images and you might need to resize or embed the diagram at multiple sizes — a wiki page, a MkDocs/Docusaurus site that doesn&amp;#39;t render Mermaid live, a print-quality document. SVG stays crisp at any zoom level and is a much smaller file for simple diagrams.&lt;/p&gt;
&lt;p&gt;Reach for &lt;strong&gt;PNG&lt;/strong&gt; for everything else in the list above — Slack, Jira, Confluence attachments, Word, and especially email — where universal support matters more than infinite scalability, and where you can&amp;#39;t be sure the destination will render an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; pointing at an SVG correctly. Our tool exports PNG at 2x scale specifically so it still looks sharp on a high-DPI display without needing SVG&amp;#39;s true vector scaling.&lt;/p&gt;
&lt;h2 id=&quot;quick-reference&quot;&gt;Quick reference&lt;/h2&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;th&gt;Native Mermaid rendering?&lt;/th&gt;
&lt;th&gt;What to do instead&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;GitHub, GitLab, Obsidian&lt;/td&gt;
&lt;td&gt;Yes, built in&lt;/td&gt;
&lt;td&gt;Nothing — paste the fence directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slack&lt;/td&gt;
&lt;td&gt;No, none&lt;/td&gt;
&lt;td&gt;Export PNG, attach/drag into message&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jira&lt;/td&gt;
&lt;td&gt;No (marketplace add-ons exist)&lt;/td&gt;
&lt;td&gt;Export PNG, attach to issue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confluence&lt;/td&gt;
&lt;td&gt;No (marketplace add-ons exist)&lt;/td&gt;
&lt;td&gt;Export PNG, attach to page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Word (.docx)&lt;/td&gt;
&lt;td&gt;No, no execution layer at all&lt;/td&gt;
&lt;td&gt;Export PNG, insert as image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email (ESP / HTML template)&lt;/td&gt;
&lt;td&gt;No, no execution layer at all&lt;/td&gt;
&lt;td&gt;Export PNG (not SVG), inline in template&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MkDocs / Docusaurus&lt;/td&gt;
&lt;td&gt;Needs a plugin, off by default&lt;/td&gt;
&lt;td&gt;Export SVG for a self-hosted static asset, or enable the plugin&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;related-reading&quot;&gt;Related reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-diagrams-flowcharts-complete-guide&quot;&gt;Markdown Diagrams and Flowcharts: Complete Guide&lt;/a&gt; — full Mermaid/PlantUML syntax reference&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-slack-formatting-guide&quot;&gt;Markdown to Slack Formatting Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-jira-wiki-markup-guide&quot;&gt;Markdown to Jira Wiki Markup Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-in-confluence-what-works&quot;&gt;Markdown in Confluence: What Actually Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-word-docx-what-converts-cleanly&quot;&gt;Markdown to Word (DOCX): What Converts Cleanly&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-email-html-newsletters-and-esps&quot;&gt;Markdown to Email HTML: Newsletters and ESPs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Try it yourself: &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-mermaid&quot;&gt;Markdown Mermaid Diagram Preview&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Markdown Word Count and Reading Time: How the Numbers Actually Get Calculated</title>
    <link href="https://www.markdowntools.com/blog/markdown-word-count-reading-time-how-it-actually-works" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/markdown-word-count-reading-time-how-it-actually-works</id>
    <published>2026-09-11T00:00:00+00:00</published>
    <updated>2026-09-11T00:00:00+00:00</updated>
    <author><name>Matt Rathbone</name></author>
    <category term="workflows"/>
    <summary>&quot;3 min read&quot; badges and word counts disagree between tools more often than you'd expect. Here's why: different words-per-minute assumptions, different handling of images, and — the part that matters most for Markdown specifically — whether the tool strips markup before counting at all.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/markdown-word-count-reading-time-how-it-actually-works">&lt;p&gt;Paste the same Markdown document into two different word-count tools and you&amp;#39;ll often get two different numbers — sometimes by a wide margin. Paste it into two different &amp;quot;reading time&amp;quot; estimators and the gap gets even bigger, because now two separate sources of disagreement stack on top of each other: how many words the tool thinks you wrote, and how fast the tool assumes a person reads.&lt;/p&gt;
&lt;p&gt;Neither number is &amp;quot;wrong,&amp;quot; exactly. They&amp;#39;re built on different assumptions, and for Markdown specifically, one of those assumptions — whether the tool strips the markup before counting — matters a lot more than most people expect. This post covers how our own &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-word-count&quot;&gt;Markdown Word Count &amp;amp; Reading Time tool&lt;/a&gt; calculates its numbers, how that compares to what a handful of other well-known platforms do, and the practical implications for anyone publishing Markdown-authored content.&lt;/p&gt;
&lt;h2 id=&quot;the-part-everyone-skips-what-counts-as-a-word-in-markdown-source&quot;&gt;The Part Everyone Skips: What Counts as a &amp;quot;Word&amp;quot; in Markdown Source&lt;/h2&gt;
&lt;p&gt;A naive word counter just splits the raw text on whitespace and counts the pieces. Applied directly to Markdown source, this produces a number that&amp;#39;s subtly wrong in both directions at once:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Markup tokens get counted as words.&lt;/strong&gt; A heading marker (&lt;code&gt;##&lt;/code&gt;), emphasis delimiters (&lt;code&gt;**&lt;/code&gt;/&lt;code&gt;_&lt;/code&gt;), a link&amp;#39;s URL, and the punctuation around an image reference all split into whitespace-separated tokens. A link like &lt;code&gt;[check the docs](https://example.com/very/long/path)&lt;/code&gt; naively counts as two &amp;quot;words&amp;quot; — &lt;code&gt;[check&lt;/code&gt; and &lt;code&gt;the...&lt;/code&gt; — when a reader only sees &amp;quot;check the docs&amp;quot; as three words of prose, and the URL isn&amp;#39;t read as words at all.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Some real prose gets undercounted.&lt;/strong&gt; A sentence broken across multiple soft-wrapped lines inside a blockquote, or text sitting inside a table cell, can get miscounted depending on how the splitter handles the surrounding &lt;code&gt;&amp;gt;&lt;/code&gt; or &lt;code&gt;|&lt;/code&gt; characters.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The practical effect is that a heavily-linked reference document or a page full of tables and images can show a meaningfully inflated or deflated word count compared to a plain-prose post of the same actual reading length — purely as an artifact of how much Markdown syntax surrounds the words, not because the tool is &amp;quot;buggy.&amp;quot; It&amp;#39;s just measuring markup density, not prose length.&lt;/p&gt;
&lt;p&gt;Our tool strips Markdown syntax first — heading markers, bold/italic delimiters, link brackets and target URLs, image syntax — before it counts anything, so the word count reflects the words a reader would actually see rendered, not the raw character soup of the source file. If you&amp;#39;re comparing a word count from our tool against a plain &lt;code&gt;wc -w&lt;/code&gt; on the raw &lt;code&gt;.md&lt;/code&gt; file, expect them to disagree, and expect the difference to grow with how markup-heavy the document is.&lt;/p&gt;
&lt;h2 id=&quot;how-our-tool-calculates-reading-time&quot;&gt;How Our Tool Calculates Reading Time&lt;/h2&gt;
&lt;p&gt;Once the stripped word count is in hand, reading time is estimated at &lt;strong&gt;238 words per minute&lt;/strong&gt; — the average adult silent-reading speed for meaningful, non-skimmed text, from a 2019 meta-analysis by Brysbaert et al. that reviewed reading-speed studies going back decades. It&amp;#39;s a plain word-count-divided-by-rate calculation with no adjustments for images, code blocks, or content type, rounded up to the next whole minute (a document estimated at 0.4 minutes still shows &amp;quot;1 min read&amp;quot; — nobody wants to see &amp;quot;0 min read&amp;quot;).&lt;/p&gt;
&lt;p&gt;The full stat panel reports ten numbers total: words, minutes to read, characters (with and without spaces), sentences (approximated from sentence-ending punctuation), paragraphs (blank-line-separated blocks, excluding headings and code blocks), headings, links, images, and fenced code blocks. All ten are computed from a single pass over the stripped and structural-element-tagged text, so they stay consistent with each other — the word count you see is the same one the reading-time estimate is based on.&lt;/p&gt;
&lt;h2 id=&quot;why-other-tools-give-you-a-different-number&quot;&gt;Why Other Tools Give You a Different Number&lt;/h2&gt;
&lt;p&gt;If you&amp;#39;ve ever seen a &amp;quot;3 min read&amp;quot; badge on a blog post and thought it felt off, here&amp;#39;s why estimates vary so much between platforms — it&amp;#39;s not just a different WPM constant, though that&amp;#39;s part of it:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The words-per-minute assumption itself varies widely.&lt;/strong&gt; Published estimates for silent reading of average material range from roughly 200 to 275+ WPM depending on the study and the type of content being read (technical or unfamiliar material is read slower than casual prose). Plenty of blogging tools and CMS plugins default to a round number like 200 WPM rather than a research-cited figure, simply because it&amp;#39;s an easy default to ship and adjust later.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Some platforms add fixed time for images on top of the word count&lt;/strong&gt;, rather than relying on WPM alone. The best-known example is Medium&amp;#39;s publicly documented reading-time algorithm: it adds 12 seconds for the first image in a post, decreasing by one second for each subsequent image down to a floor of 3 seconds per image, on top of a roughly 265-WPM word-based estimate. The reasoning is that a reader&amp;#39;s eye pauses on an image for a real, measurable amount of time that a pure word count doesn&amp;#39;t capture at all — the more images a post has, the more this pushes the total above what word count alone would suggest. Several open-source reading-time libraries (used by a number of blogging platforms and static-site plugins) borrow this same image-time-plus-word-time approach rather than inventing their own.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Some don&amp;#39;t strip markup, or strip it differently.&lt;/strong&gt; A platform&amp;#39;s reading-time calculator that runs on already-rendered HTML text (stripping HTML tags rather than Markdown syntax) will produce a different word count than one that runs on raw Markdown source and strips Markdown syntax — the two aren&amp;#39;t measuring quite the same input, even when both claim to be counting &amp;quot;the words in the post.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A few platforms don&amp;#39;t estimate reading time at all&lt;/strong&gt;, and it&amp;#39;s added manually by the author, via a separate plugin, or not shown at all — so &amp;quot;no reading time badge&amp;quot; doesn&amp;#39;t necessarily mean &amp;quot;short post,&amp;quot; just that the platform doesn&amp;#39;t calculate one.&lt;/p&gt;
&lt;p&gt;None of this makes any single number more &amp;quot;correct&amp;quot; than another — they&amp;#39;re all reasonable estimates built on different, defensible assumptions. But it explains why you shouldn&amp;#39;t expect two tools to agree, and why a reading-time badge is a rough signal, not a precise measurement.&lt;/p&gt;
&lt;h2 id=&quot;quick-reference-what-varies-between-reading-time-approaches&quot;&gt;Quick Reference: What Varies Between Reading-Time Approaches&lt;/h2&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;Factor&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;What it affects&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;WPM assumption (commonly 200–275)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;The baseline word-count-to-minutes conversion; higher WPM = shorter estimated time for the same word count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Markup-stripping before counting&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Whether Markdown/HTML syntax inflates or deflates the &amp;quot;word&amp;quot; count itself, before WPM is even applied&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Fixed per-image time (Medium-style algorithms and the libraries that copy the approach)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Adds extra seconds per image on top of the word-based estimate, so image-heavy posts read as longer than their word count alone suggests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Content-type adjustment&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Some estimators don&amp;#39;t adjust for this at all (ours doesn&amp;#39;t); others informally assume technical/code-heavy content is read slower, though this is far less standardized across tools than the WPM constant itself&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Whether reading time is shown at all&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Some platforms simply don&amp;#39;t calculate or display it, which isn&amp;#39;t itself a signal about post length&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;practical-implications-for-writers&quot;&gt;Practical Implications for Writers&lt;/h2&gt;
&lt;p&gt;None of this is purely academic if you&amp;#39;re publishing regularly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reading-time badges are directional, not precise.&lt;/strong&gt; Treat &amp;quot;5 min read&amp;quot; as &amp;quot;this will take you a few minutes,&amp;quot; not a literal stopwatch prediction — actual reading speed for any individual reader varies enormously based on familiarity with the topic, whether they&amp;#39;re skimming or reading closely, and whether the content is prose or dense technical material.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Word count is still useful as a length gauge&lt;/strong&gt;, independent of the reading-time conversion — the raw number tells you whether a post is thin (worth expanding) or sprawling (worth tightening), which is a more actionable signal than the derived minutes figure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If you&amp;#39;re comparing counts across tools, check whether markup is stripped.&lt;/strong&gt; A count taken directly off the raw &lt;code&gt;.md&lt;/code&gt; file (e.g., a quick &lt;code&gt;wc -w&lt;/code&gt;) will run higher than a count taken after Markdown syntax is stripped, and the gap grows with how link- and formatting-heavy the document is — this is the single most common source of &amp;quot;why don&amp;#39;t these two numbers match&amp;quot; confusion.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Structural stats (headings, code blocks, links, images) are often more useful than the word count alone&lt;/strong&gt; when you&amp;#39;re doing an editorial pass — a post with far more headings than paragraphs might be under-developed per section; one with a high image count but few words of surrounding prose might need more explanatory text around each image.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;use-cases&quot;&gt;Use Cases&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pre-publish checks&lt;/strong&gt;: confirm a draft hits a target length before it goes out, without needing to open a separate word processor&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;README and documentation length audits&lt;/strong&gt;: spot check whether a README has grown unwieldy, or whether a section is too thin to be useful&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Editorial workflows&lt;/strong&gt;: use the structural stats (headings, paragraphs, code blocks) alongside the word count to catch under-developed sections, not just short posts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-tool sanity checks&lt;/strong&gt;: if a CMS&amp;#39;s built-in word count disagrees with ours, the tables above cover the most likely reasons why, rather than assuming either number is broken&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;related-tools&quot;&gt;Related Tools&lt;/h2&gt;
&lt;p&gt;If you&amp;#39;re checking a document&amp;#39;s length and structure, our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-linter&quot;&gt;Markdown Linter&lt;/a&gt; catches heading-hierarchy and formatting issues the word-count stats won&amp;#39;t surface on their own, and the &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-editor&quot;&gt;Markdown Live Preview Editor&lt;/a&gt; lets you see exactly how the stripped, rendered version of your document looks before you publish. If your document is long enough that navigation matters, our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-toc&quot;&gt;Table of Contents Generator&lt;/a&gt; builds one automatically from your headings.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Markdown Image Sizing and Alignment Across Platforms: What Actually Works</title>
    <link href="https://www.markdowntools.com/blog/markdown-image-sizing-alignment-across-platforms" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/markdown-image-sizing-alignment-across-platforms</id>
    <published>2026-09-04T00:00:00+00:00</published>
    <updated>2026-09-04T00:00:00+00:00</updated>
    <author><name>Matt Rathbone</name></author>
    <category term="workflows"/>
    <summary>Wrapping an image in a sized, centered &lt;img&gt; tag works great in GitHub — but paste that same Markdown into Notion or Slack and the HTML just vanishes. Here's which platforms honor inline HTML for images, which have their own separate syntax, and which need attachments instead.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/markdown-image-sizing-alignment-across-platforms">&lt;p&gt;Standard Markdown&amp;#39;s image syntax — &lt;code&gt;![alt](url)&lt;/code&gt; — has no way to control size or alignment. There&amp;#39;s no &lt;code&gt;width=&lt;/code&gt;, no &lt;code&gt;center&lt;/code&gt;, nothing. So the moment you need a smaller image, or one centered on the page instead of jammed against the left margin, you reach for a workaround: wrap it in an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag with &lt;code&gt;width&lt;/code&gt;/&lt;code&gt;height&lt;/code&gt; attributes, or drop it in a &lt;code&gt;&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;/code&gt;. That workaround works beautifully on some platforms and does absolutely nothing on others, because it depends entirely on whether the destination renderer passes raw HTML through untouched — and that&amp;#39;s a much less consistent behavior across the Markdown ecosystem than most people assume.&lt;/p&gt;
&lt;p&gt;This guide covers where the HTML-in-Markdown technique actually works, where a platform has its own separate, non-HTML syntax for the same job, and where images need to go through the platform&amp;#39;s own attachment mechanism instead. If you want the full rundown of resizing syntax first, our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-change-image-size&quot;&gt;guide to changing image size in Markdown&lt;/a&gt; and our deeper &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-image-alignment-positioning-complete-guide&quot;&gt;image alignment and positioning guide&lt;/a&gt; cover that ground in detail; this post assumes you know the basic techniques and focuses on where each one is actually honored. And if you&amp;#39;d rather generate a correctly-sized, aligned image snippet — with an optional caption or link wrapper — without hand-writing the HTML, our new &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-image-formatter&quot;&gt;Markdown Image Formatter&lt;/a&gt; does that for you.&lt;/p&gt;
&lt;h2 id=&quot;the-core-technique-inline-html&quot;&gt;The Core Technique: Inline HTML&lt;/h2&gt;
&lt;p&gt;The workaround every image-sizing tutorial teaches is the same one:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-html&quot;&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;src&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;diagram.png&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;alt&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Architecture diagram&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;width&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;500&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;

&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;align&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;center&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;src&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;diagram.png&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;alt&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Architecture diagram&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;width&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;500&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;div&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Whether this renders as a resized, centered image or as a chunk of literal, unrendered text depends entirely on one thing: does the destination treat the Markdown source as &amp;quot;Markdown with inline HTML allowed,&amp;quot; or does it sanitize/strip HTML before or during rendering? That single distinction is the whole story for most of the platforms below.&lt;/p&gt;
&lt;h2 id=&quot;github-and-gitlab-full-support&quot;&gt;GitHub and GitLab: Full Support&lt;/h2&gt;
&lt;p&gt;GitHub and GitLab both parse inline HTML inside Markdown files — READMEs, wiki pages, issues, and pull requests all honor &lt;code&gt;&amp;lt;img width=&amp;quot;...&amp;quot;&amp;gt;&lt;/code&gt; and alignment wrappers like &lt;code&gt;&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;p align=&amp;quot;center&amp;quot;&amp;gt;&lt;/code&gt;. This is the environment the workaround was basically invented for, and it&amp;#39;s why you see it everywhere in open-source READMEs with a centered logo at the top.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-html&quot;&gt;&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;align&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;center&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;&lt;span class=&quot;hljs-name&quot;&gt;img&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;src&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;logo.png&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;alt&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;Project logo&amp;quot;&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;width&lt;/span&gt;=&lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;300&amp;quot;&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;hljs-tag&quot;&gt;&amp;lt;/&lt;span class=&quot;hljs-name&quot;&gt;p&lt;/span&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Both platforms do strip genuinely dangerous content — &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags and inline event-handler attributes like &lt;code&gt;onclick&lt;/code&gt; don&amp;#39;t survive — but plain presentational attributes on an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; or a &lt;code&gt;&amp;lt;div align&amp;gt;&lt;/code&gt; wrapper pass through untouched in both README files and rendered issue/PR comments.&lt;/p&gt;
&lt;h2 id=&quot;notion-strips-html-uses-its-own-block-instead&quot;&gt;Notion: Strips HTML, Uses Its Own Block Instead&lt;/h2&gt;
&lt;p&gt;Notion does not render inline HTML in imported or pasted Markdown — as covered in our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-for-notion-what-actually-works&quot;&gt;Notion companion post&lt;/a&gt;, raw HTML blocks are stripped on import, full stop. An &lt;code&gt;&amp;lt;img width=&amp;quot;300&amp;quot;&amp;gt;&lt;/code&gt; tag doesn&amp;#39;t get sanitized down to a plain image; it just disappears, or at best leaves behind orphaned text.&lt;/p&gt;
&lt;p&gt;Instead, Notion has its own native image block: drag an image in, or paste an image URL on its own line, and Notion creates an image block with a drag handle on the corner for resizing and an alignment control (left/center/full-width) in the block&amp;#39;s own toolbar. This is a completely different mechanism from Markdown syntax — there&amp;#39;s no text-based way to specify the size in the source document, because sizing happens as a manual, post-import editing step inside Notion&amp;#39;s own UI.&lt;/p&gt;
&lt;h2 id=&quot;slack-strips-html-no-sizing-control-at-all&quot;&gt;Slack: Strips HTML, No Sizing Control At All&lt;/h2&gt;
&lt;p&gt;Slack&amp;#39;s mrkdwn format strips raw HTML entirely, matching what our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-slack-formatting-guide&quot;&gt;Markdown to Slack companion post&lt;/a&gt; already documents for the format generally — there&amp;#39;s no HTML passthrough to lean on here either. But unlike Notion, Slack doesn&amp;#39;t have an equivalent native resize/align control for images sent as part of a message&amp;#39;s text at all. A Markdown image reference in mrkdwn doesn&amp;#39;t render inline as an image — it&amp;#39;s ignored as text.&lt;/p&gt;
&lt;p&gt;To actually get an image to display in Slack, you need a separate &lt;code&gt;image&lt;/code&gt; block in Block Kit (the API for building rich Slack messages), which takes an &lt;code&gt;image_url&lt;/code&gt; and &lt;code&gt;alt_text&lt;/code&gt; — no width, height, or alignment parameter exists on that block type. If you&amp;#39;re pasting into the message composer by hand rather than going through the API, you attach the image file directly to the message instead of referencing it in text. Either way, there&amp;#39;s no sizing or alignment control at all through Slack&amp;#39;s own message formatting — full stop.&lt;/p&gt;
&lt;h2 id=&quot;obsidian-its-own-embed-syntax-not-html&quot;&gt;Obsidian: Its Own Embed Syntax, Not HTML&lt;/h2&gt;
&lt;p&gt;Obsidian takes a third approach entirely: it has its own embed syntax, separate from both standard Markdown images and inline HTML, and it does support sizing directly in plain text:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;![[diagram.png]]
![[diagram.png|300]]
![[diagram.png|300x150]]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The double-bracket embed (the same wiki-link syntax Obsidian uses for linking between notes) accepts a pipe-separated width in pixels, or a &lt;code&gt;widthxheight&lt;/code&gt; pair for both dimensions. This is a real, working, plain-text mechanism — no HTML required — but it&amp;#39;s Obsidian-specific: those pipe-delimited numbers mean nothing to any other Markdown renderer, and a file using &lt;code&gt;![[diagram.png|300]]&lt;/code&gt; synced or copied out to GitHub or a static site generator will show up as a broken, literal wiki-link rather than a sized image.&lt;/p&gt;
&lt;p&gt;Alignment is a separate story and less standardized: Obsidian&amp;#39;s core embed syntax has no built-in center/left/right keyword the way its &lt;code&gt;|300&lt;/code&gt; width modifier exists. Centering an embedded image typically means either a CSS snippet targeting &lt;code&gt;.markdown-rendered img&lt;/code&gt; in Obsidian&amp;#39;s own appearance settings, or a community plugin (several exist specifically for this). Standard &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;-tag HTML does also work inside Obsidian&amp;#39;s own renderer if you&amp;#39;d rather use the same technique as GitHub — Obsidian doesn&amp;#39;t strip inline HTML — but it&amp;#39;s not the idiomatic Obsidian way, and text written that way is really written for the HTML-friendly platforms, with Obsidian just happening to also render it.&lt;/p&gt;
&lt;h2 id=&quot;jekyll-kramdown-mkdocs-and-docusaurus-confirmed-not-assumed&quot;&gt;Jekyll (Kramdown), MkDocs, and Docusaurus: Confirmed, Not Assumed&lt;/h2&gt;
&lt;p&gt;Since this blog itself runs on Jekyll with kramdown, we can confirm this one rather than guess: kramdown passes inline HTML through by default — that&amp;#39;s exactly how the raw &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;/&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; snippets elsewhere on this site&amp;#39;s own posts render correctly — so the standard HTML-sizing workaround works here without any special configuration.&lt;/p&gt;
&lt;p&gt;MkDocs, built on Python-Markdown, also allows raw HTML through by default — Python-Markdown&amp;#39;s core design treats HTML blocks as pass-through content unless you&amp;#39;ve deliberately enabled an extension that sanitizes it, which isn&amp;#39;t part of a stock MkDocs or MkDocs Material setup. So the same &lt;code&gt;&amp;lt;img width=&amp;quot;...&amp;quot;&amp;gt;&lt;/code&gt; technique works there too.&lt;/p&gt;
&lt;p&gt;Docusaurus is the one genuine exception worth calling out, because it&amp;#39;s not just &amp;quot;Markdown&amp;quot; under the hood — it compiles content through MDX, which parses HTML-like syntax as JSX rather than passing it through as literal HTML text. A plain &lt;code&gt;&amp;lt;img width=&amp;quot;300&amp;quot;&amp;gt;&lt;/code&gt; tag generally still works, since it&amp;#39;s valid JSX syntax too, but MDX is considerably stricter about HTML syntax than kramdown or Python-Markdown are: unclosed tags, HTML comments, and certain attribute-quoting patterns that browsers happily tolerate can cause an outright MDX compile error rather than being ignored or downgraded gracefully. If you&amp;#39;re moving a directory of existing image-heavy Markdown into a Docusaurus site, this is worth testing rather than assuming a clean HTML-passthrough experience.&lt;/p&gt;
&lt;h2 id=&quot;confluence-and-jira-neither-html-nor-a-text-syntax&quot;&gt;Confluence and Jira: Neither HTML Nor a Text Syntax&lt;/h2&gt;
&lt;p&gt;Confluence is the platform where the HTML workaround fails most completely. Our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-in-confluence-what-works&quot;&gt;Confluence companion post&lt;/a&gt; already documents that pasting Markdown mixed with HTML gets the HTML stripped or escaped during the paste-conversion step — an &lt;code&gt;&amp;lt;img width=&amp;quot;300&amp;quot;&amp;gt;&lt;/code&gt; tag doesn&amp;#39;t survive being pasted as part of a Markdown block at all. Confluence does auto-convert a plain &lt;code&gt;![alt](url)&lt;/code&gt; image reference into an embedded image on paste, but from there, resizing and alignment are done as a manual step in Confluence&amp;#39;s own rich-text editor after the image lands on the page — click the image, drag a resize handle or pick an alignment option from its floating toolbar. There&amp;#39;s no Markdown-adjacent text syntax for it at all, HTML or otherwise.&lt;/p&gt;
&lt;p&gt;Jira is similar but starts from a different syntax entirely. Jira wiki markup&amp;#39;s own image notation is &lt;code&gt;!attachment.png!&lt;/code&gt;, with modifiers like &lt;code&gt;!attachment.png|thumbnail!&lt;/code&gt; — not Markdown syntax, and not HTML, as our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-jira-wiki-markup-guide&quot;&gt;Jira wiki markup companion post&lt;/a&gt; documents for the format generally. Sizing beyond the handful of built-in modifiers isn&amp;#39;t available through wiki markup text at all. In the modern Jira Cloud rich-text editor (outside classic wiki-markup-based projects), the equivalent workflow is the same as Confluence: the image has to actually be attached to the issue, then resized by dragging a handle in the editor — there&amp;#39;s no way to type your way to a specific pixel width.&lt;/p&gt;
&lt;h2 id=&quot;quick-reference&quot;&gt;Quick Reference&lt;/h2&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;Platform&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;Sizing mechanism&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;HTML passthrough?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;GitHub / GitLab&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;&amp;lt;img width&amp;gt;&lt;/code&gt; / &lt;code&gt;&amp;lt;div align&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Notion&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Native image block, drag-resize + alignment toolbar&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;No — HTML stripped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Slack&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;None via text; separate Block Kit &lt;code&gt;image&lt;/code&gt; block (no size param)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;No — HTML stripped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Obsidian&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;![[file.png|300]]&lt;/code&gt; embed syntax (own convention)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Yes (but not idiomatic)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Jekyll (kramdown)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;&amp;lt;img width&amp;gt;&lt;/code&gt; / &lt;code&gt;&amp;lt;div align&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;MkDocs (Python-Markdown)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;&amp;lt;img width&amp;gt;&lt;/code&gt; / &lt;code&gt;&amp;lt;div align&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Docusaurus (MDX)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;&amp;lt;img width&amp;gt;&lt;/code&gt; — parsed as JSX, stricter syntax rules&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Mostly, with caveats&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Confluence&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;Manual resize via editor after paste/attach&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;No — stripped on paste&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;Jira&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;&lt;code&gt;!file.png|thumbnail!&lt;/code&gt; modifiers, or manual resize (Cloud editor)&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;what-this-means-in-practice&quot;&gt;What This Means in Practice&lt;/h2&gt;
&lt;p&gt;If you&amp;#39;re writing documentation that lives primarily on GitHub, GitLab, a Jekyll site, or MkDocs, the &lt;code&gt;&amp;lt;img width&amp;gt;&lt;/code&gt; / &lt;code&gt;&amp;lt;div align=&amp;quot;center&amp;quot;&amp;gt;&lt;/code&gt; technique is safe to rely on — it&amp;#39;s the same technique across all four, and it&amp;#39;s the one most existing tutorials (including our own) teach as the default answer. Docusaurus generally tolerates it too, just test your specific images rather than assuming.&lt;/p&gt;
&lt;p&gt;The moment content needs to also live in Notion, Slack, Confluence, or Jira, that same HTML gets silently discarded or simply never applies, and there&amp;#39;s no universal substitute — each of those four has picked a genuinely different mechanism (a native resizable block, a separate API block type with no size control, a post-paste manual editor step, or Jira&amp;#39;s own attachment modifiers). If your workflow moves the same source Markdown across several of these platforms, plan on images being one of the places you can&amp;#39;t avoid a platform-specific manual step, rather than something you can format once in the source file and have travel cleanly everywhere. For the general version of this problem — what else breaks when Markdown crosses platforms — see our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-platform-compatibility-guide&quot;&gt;platform compatibility guide&lt;/a&gt; and our &lt;a href=&quot;https://www.markdowntools.com/blog/why-your-markdown-isnt-rendering&quot;&gt;troubleshooting guide for Markdown that isn&amp;#39;t rendering&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;related-reading&quot;&gt;Related Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-change-image-size&quot;&gt;How to Change Image Size in Markdown&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-image-alignment-positioning-complete-guide&quot;&gt;Markdown Image Alignment and Positioning: Complete Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-platform-compatibility-guide&quot;&gt;Markdown Across Platforms: What Actually Works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/why-your-markdown-isnt-rendering&quot;&gt;Why Your Markdown Isn&amp;#39;t Rendering&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/markdown-image-formatter&quot;&gt;Markdown Image Formatter tool&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Markdown Callouts and Admonitions Across Platforms: What Actually Renders</title>
    <link href="https://www.markdowntools.com/blog/markdown-callouts-admonitions-across-platforms" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/markdown-callouts-admonitions-across-platforms</id>
    <published>2026-08-28T00:00:00+00:00</published>
    <updated>2026-08-28T00:00:00+00:00</updated>
    <author><name>Matt Rathbone</name></author>
    <category term="workflows"/>
    <summary>GitHub's &gt; [!NOTE] alert syntax, Obsidian's callouts, and MkDocs's admonition extension all look like variations on a theme — but they're three genuinely different syntaxes that mostly don't cross-render. Here's what works where.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/markdown-callouts-admonitions-across-platforms">&lt;p&gt;Type &lt;code&gt;&amp;gt; [!NOTE]&lt;/code&gt; into a Markdown file and on GitHub you get a styled, icon-prefixed callout box. Paste that exact same text into a plain CommonMark renderer, a Jekyll site with no plugin, or an old-school static blog, and you get an ordinary blockquote with the literal text &lt;code&gt;[!NOTE]&lt;/code&gt; sitting inside it. This trips people up constantly, because GitHub&amp;#39;s alert syntax is popular enough — and looks native enough — that it&amp;#39;s easy to assume it&amp;#39;s part of Markdown itself. It isn&amp;#39;t. It&amp;#39;s a GitHub- and GitLab-specific extension, and it&amp;#39;s just one of at least three unrelated callout syntaxes in active use depending on which platform or static site generator you&amp;#39;re targeting.&lt;/p&gt;
&lt;p&gt;This guide covers what each syntax actually looks like, which platforms understand it natively, which need a plugin or config change, and — the part most syntax tutorials skip — what happens when you paste one platform&amp;#39;s syntax into a renderer that doesn&amp;#39;t understand it. If you want the full syntax reference and platform-by-platform examples first, our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-admonitions-callouts-complete-guide&quot;&gt;complete guide to admonitions and callouts&lt;/a&gt; and our narrower &lt;a href=&quot;https://www.markdowntools.com/blog/add-a-warning-box-alert-in-markdown&quot;&gt;guide to warning boxes and alerts&lt;/a&gt; cover that ground; this post assumes you already know the basic syntax and focuses on where it actually renders. And if you just want to generate correctly-formatted callout syntax for your specific target platform without memorizing four different formats, our new &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-callouts&quot;&gt;Markdown Callout Generator&lt;/a&gt; does that.&lt;/p&gt;
&lt;h2 id=&quot;github-and-gitlab-alerts-native-but-not-commonmark&quot;&gt;GitHub and GitLab Alerts: Native, But Not CommonMark&lt;/h2&gt;
&lt;p&gt;GitHub&amp;#39;s alert syntax — a blockquote whose first line is one of five fixed keywords in brackets — is the one most people encounter first:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; [!NOTE]&lt;/span&gt;
&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; Useful information that users should know, even when skimming.&lt;/span&gt;

&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; [!TIP]&lt;/span&gt;
&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; Helpful advice for doing things better or more easily.&lt;/span&gt;

&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; [!IMPORTANT]&lt;/span&gt;
&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; Key information users need to know to achieve their goal.&lt;/span&gt;

&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; [!WARNING]&lt;/span&gt;
&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; Urgent info that needs immediate attention to avoid problems.&lt;/span&gt;

&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; [!CAUTION]&lt;/span&gt;
&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; Advises about risks or negative outcomes of certain actions.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Those exact five types — no more, no fewer — are the only ones GitHub&amp;#39;s renderer recognizes. Typing &lt;code&gt;&amp;gt; [!DANGER]&lt;/code&gt; or &lt;code&gt;&amp;gt; [!INFO]&lt;/code&gt; doesn&amp;#39;t error, it just falls back to being an ordinary blockquote with literal bracket text, because those aren&amp;#39;t in GitHub&amp;#39;s fixed vocabulary. GitLab adopted the identical syntax and the same five types, so anything written for one generally displays correctly on the other.&lt;/p&gt;
&lt;p&gt;The important gotcha: this is a renderer-level feature, not a Markdown-spec feature. It works in READMEs, issues, PRs, wikis, and discussions on github.com and gitlab.com specifically, because those two platforms&amp;#39; own Markdown renderers added special-case handling for the &lt;code&gt;[!KEYWORD]&lt;/code&gt; pattern inside a blockquote. Take that same text to a plain CommonMark or GFM-spec-only renderer, a Jekyll/kramdown site with no custom handling, an email client, or a PDF exporter, and you get a plain blockquote containing the literal string &lt;code&gt;[!NOTE]&lt;/code&gt; — no styling, no icon, no color. It&amp;#39;s still readable, just not what you intended.&lt;/p&gt;
&lt;h2 id=&quot;obsidian-callouts-a-similar-look-a-different-larger-vocabulary&quot;&gt;Obsidian Callouts: A Similar Look, a Different (Larger) Vocabulary&lt;/h2&gt;
&lt;p&gt;Obsidian&amp;#39;s callout syntax is close enough to GitHub&amp;#39;s to cause real confusion, but it&amp;#39;s a separate implementation with its own, much larger type vocabulary and an extra feature GitHub&amp;#39;s alerts don&amp;#39;t have at all:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; [!note] Project Setup&lt;/span&gt;
&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; Follow these steps to configure your environment.&lt;/span&gt;

&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; [!warning] Version Compatibility&lt;/span&gt;
&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; This plugin requires Obsidian 0.15.0 or higher.&lt;/span&gt;

&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; [!tip]- Collapsible Tip&lt;/span&gt;
&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; The minus sign after the type makes this foldable, closed by default.&lt;/span&gt;
&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; A plus sign instead makes it foldable, but open by default.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Obsidian recognizes over a dozen built-in types (&lt;code&gt;note&lt;/code&gt;, &lt;code&gt;abstract&lt;/code&gt;/&lt;code&gt;summary&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;todo&lt;/code&gt;, &lt;code&gt;tip&lt;/code&gt;/&lt;code&gt;hint&lt;/code&gt;, &lt;code&gt;success&lt;/code&gt;/&lt;code&gt;check&lt;/code&gt;, &lt;code&gt;question&lt;/code&gt;, &lt;code&gt;warning&lt;/code&gt;/&lt;code&gt;caution&lt;/code&gt;, &lt;code&gt;failure&lt;/code&gt;/&lt;code&gt;fail&lt;/code&gt;, &lt;code&gt;danger&lt;/code&gt;/&lt;code&gt;error&lt;/code&gt;, &lt;code&gt;bug&lt;/code&gt;, &lt;code&gt;example&lt;/code&gt;, &lt;code&gt;quote&lt;/code&gt;), several of which are simply aliases for each other, plus a fold indicator (&lt;code&gt;-&lt;/code&gt;/&lt;code&gt;+&lt;/code&gt; right after the closing bracket) that GitHub&amp;#39;s syntax has no equivalent for at all. Obsidian is also case-insensitive on the type keyword and lets you supply a custom title after it — &lt;code&gt;[!warning] Version Compatibility&lt;/code&gt; — where GitHub&amp;#39;s alerts always use the fixed keyword itself as the visible title with no override.&lt;/p&gt;
&lt;p&gt;Because the outer shape (&lt;code&gt;&amp;gt; [!type]&lt;/code&gt;) looks so similar to GitHub&amp;#39;s, it&amp;#39;s easy to assume the two are interchangeable. They&amp;#39;re not: paste &lt;code&gt;[!tip]-&lt;/code&gt; into GitHub and you get a plain blockquote reading &amp;quot;[!tip]- Collapsible Tip&amp;quot; with no folding behavior, because GitHub&amp;#39;s renderer only pattern-matches its own five fixed keywords and has no concept of the fold suffix. Paste &lt;code&gt;[!IMPORTANT]&lt;/code&gt; into Obsidian and it renders as a plain, unstyled callout box with no icon, because &lt;code&gt;important&lt;/code&gt; isn&amp;#39;t one of Obsidian&amp;#39;s built-in types (Obsidian falls back to a generic default box rather than an ordinary blockquote, but it&amp;#39;s still not the styled result you&amp;#39;d get on GitHub).&lt;/p&gt;
&lt;h2 id=&quot;mkdocs-a-real-extension-config-required-indentation-sensitive&quot;&gt;MkDocs: A Real Extension, Config Required, Indentation-Sensitive&lt;/h2&gt;
&lt;p&gt;MkDocs (via Python-Markdown&amp;#39;s &lt;code&gt;admonition&lt;/code&gt; extension, or the more capable &lt;code&gt;pymdownx.details&lt;/code&gt; for foldable versions) uses a completely different syntax shape — no blockquote &lt;code&gt;&amp;gt;&lt;/code&gt; at all:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;!!! note &amp;quot;Configuration Requirements&amp;quot;
&lt;span class=&quot;hljs-code&quot;&gt;    This feature requires Python 3.8+ and the following dependencies.
&lt;/span&gt;
??? tip &amp;quot;Click to expand&amp;quot;
&lt;span class=&quot;hljs-code&quot;&gt;    Using `???` instead of `!!!` makes the block collapsible, closed by default.
    `???+` makes it collapsible but open by default.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Three things make this meaningfully different from the blockquote-based syntaxes above. First, it does nothing at all unless you&amp;#39;ve explicitly enabled it — add &lt;code&gt;admonition&lt;/code&gt; (and &lt;code&gt;pymdownx.details&lt;/code&gt; if you want the foldable &lt;code&gt;???&lt;/code&gt; variant) under &lt;code&gt;markdown_extensions:&lt;/code&gt; in &lt;code&gt;mkdocs.yml&lt;/code&gt;. Without that config line, &lt;code&gt;!!! note&lt;/code&gt; renders as a literal paragraph starting with three exclamation marks; there&amp;#39;s no fallback styling of any kind. Second, the content that belongs inside the block must be indented four spaces under the &lt;code&gt;!!!&lt;/code&gt;/&lt;code&gt;???&lt;/code&gt; line — get the indentation wrong and Python-Markdown simply doesn&amp;#39;t associate that paragraph with the admonition, silently dropping it back to being a regular, un-boxed paragraph directly below an empty admonition. Third, the type vocabulary is open-ended rather than fixed: &lt;code&gt;!!! note&lt;/code&gt;, &lt;code&gt;!!! danger&lt;/code&gt;, &lt;code&gt;!!! example&lt;/code&gt;, or any custom type name you&amp;#39;ve styled via CSS all work, since the extension doesn&amp;#39;t validate against a fixed keyword list the way GitHub&amp;#39;s alerts do.&lt;/p&gt;
&lt;p&gt;None of GitHub&amp;#39;s, GitLab&amp;#39;s, or Obsidian&amp;#39;s blockquote-based syntax does anything special in MkDocs — a pasted &lt;code&gt;&amp;gt; [!NOTE]&lt;/code&gt; block just renders as a plain, unstyled blockquote with visible bracket text, exactly like taking GitHub syntax to any other non-GitHub renderer.&lt;/p&gt;
&lt;h2 id=&quot;docusaurus-a-third-shape-entirely--fenced-directives&quot;&gt;Docusaurus: A Third Shape Entirely — Fenced Directives&lt;/h2&gt;
&lt;p&gt;Docusaurus (and MDX-based tooling more broadly) uses yet another format, based on triple-colon fenced directives rather than either blockquotes or indented blocks:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;:::note
Useful information that users should know.
:::

:::tip[Optional Custom Title]
Helpful advice, with an optional title in square brackets.
:::

:::danger
Highlights dangerous actions or negative outcomes.
:::
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Docusaurus supports five built-in types (&lt;code&gt;note&lt;/code&gt;, &lt;code&gt;tip&lt;/code&gt;, &lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warning&lt;/code&gt;, &lt;code&gt;danger&lt;/code&gt;) and works out of the box with no plugin config, since the directive syntax is built into Docusaurus&amp;#39;s MDX processing pipeline directly. The syntax shape — a fence of three colons opening and closing the block, with the type name immediately after the opening fence — has nothing in common with GitHub&amp;#39;s blockquote convention or MkDocs&amp;#39;s indented-content convention, so none of those interoperate with this one either. Paste &lt;code&gt;:::note&lt;/code&gt; content into GitHub or a plain CommonMark renderer and you get exactly what it looks like: a paragraph starting and ending with three literal colons, no box, no styling.&lt;/p&gt;
&lt;h2 id=&quot;quick-reference&quot;&gt;Quick Reference&lt;/h2&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Syntax shape&lt;/th&gt;
&lt;th&gt;Native or needs config&lt;/th&gt;
&lt;th&gt;Fold/collapse support&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt; [!TYPE]&lt;/code&gt; blockquote, 5 fixed types&lt;/td&gt;
&lt;td&gt;Native, no config&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GitLab&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt; [!TYPE]&lt;/code&gt; blockquote, same 5 types as GitHub&lt;/td&gt;
&lt;td&gt;Native, no config&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Obsidian&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt; [!type]&lt;/code&gt; blockquote, 12+ types, custom titles&lt;/td&gt;
&lt;td&gt;Native, no config&lt;/td&gt;
&lt;td&gt;Yes — &lt;code&gt;[!type]-&lt;/code&gt; / &lt;code&gt;[!type]+&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MkDocs (Material or base theme)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;!!! type &amp;quot;Title&amp;quot;&lt;/code&gt;, 4-space indented content&lt;/td&gt;
&lt;td&gt;Needs &lt;code&gt;admonition&lt;/code&gt; extension enabled in &lt;code&gt;mkdocs.yml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes, with &lt;code&gt;pymdownx.details&lt;/code&gt; — &lt;code&gt;???&lt;/code&gt;/&lt;code&gt;???+&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Docusaurus&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;:::type&lt;/code&gt; ... &lt;code&gt;:::&lt;/code&gt; fenced directive&lt;/td&gt;
&lt;td&gt;Native (built into MDX pipeline)&lt;/td&gt;
&lt;td&gt;No (use a separate &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; for that)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sphinx / reStructuredText&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.. type::&lt;/code&gt; directive (not Markdown at all)&lt;/td&gt;
&lt;td&gt;Native to RST&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Plain CommonMark / unconfigured static sites&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;None of the above render specially — all fall back to a plain blockquote or paragraph with literal syntax visible&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;what-doesnt-cross-render-concretely&quot;&gt;What Doesn&amp;#39;t Cross-Render, Concretely&lt;/h2&gt;
&lt;p&gt;The pattern across every combination above is the same: &lt;strong&gt;each platform&amp;#39;s callout syntax is invisible to every other platform&amp;#39;s renderer&lt;/strong&gt;, and none of them error — they all degrade to plain, unstyled Markdown with the literal syntax characters left visible in the text. Concretely:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GitHub-alert syntax (&lt;code&gt;&amp;gt; [!NOTE]&lt;/code&gt;) pasted into a MkDocs site does &lt;strong&gt;not&lt;/strong&gt; become a styled admonition — MkDocs&amp;#39;s &lt;code&gt;admonition&lt;/code&gt; extension has no idea what a &lt;code&gt;[!NOTE]&lt;/code&gt; blockquote is, so it renders as an ordinary blockquote with visible bracket text.&lt;/li&gt;
&lt;li&gt;MkDocs syntax (&lt;code&gt;!!! note&lt;/code&gt;) pasted into GitHub does &lt;strong&gt;not&lt;/strong&gt; become a callout — GitHub&amp;#39;s renderer only pattern-matches its own five bracketed keywords inside blockquotes, and &lt;code&gt;!!! note&lt;/code&gt; isn&amp;#39;t even inside a blockquote to begin with, so it just renders as a plain paragraph starting with &lt;code&gt;!!!&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Docusaurus&amp;#39;s &lt;code&gt;:::note&lt;/code&gt; fences pasted anywhere else — GitHub, Obsidian, MkDocs, plain Jekyll — render as plain text with visible colons, since the triple-colon-fence convention is specific to Docusaurus&amp;#39;s MDX processing.&lt;/li&gt;
&lt;li&gt;Obsidian&amp;#39;s extended type vocabulary (&lt;code&gt;[!bug]&lt;/code&gt;, &lt;code&gt;[!question]&lt;/code&gt;, &lt;code&gt;[!abstract]&lt;/code&gt;) pasted into GitHub renders as an unstyled blockquote, since those types fall outside GitHub&amp;#39;s fixed five.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;#39;re writing documentation that has to look right on more than one of these platforms — a README that also gets built into a MkDocs or Docusaurus site, for instance — plain HTML &lt;code&gt;&amp;lt;div class=&amp;quot;admonition&amp;quot;&amp;gt;&lt;/code&gt; markup (styled with your own CSS) is still the only genuinely universal fallback, at the cost of losing GitHub&amp;#39;s and Obsidian&amp;#39;s zero-config native styling. There&amp;#39;s no single syntax that renders correctly everywhere; you&amp;#39;re choosing per target platform, not writing once.&lt;/p&gt;
&lt;h2 id=&quot;related-reading&quot;&gt;Related Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-admonitions-callouts-complete-guide&quot;&gt;Markdown Admonitions and Callouts: Complete Guide&lt;/a&gt; — the fuller syntax reference and platform examples this post assumes as background&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/add-a-warning-box-alert-in-markdown&quot;&gt;How to Create a Warning Box Alert in Markdown&lt;/a&gt; — the narrower blockquote-vs-HTML warning box case specifically&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-platform-compatibility-guide&quot;&gt;Markdown Across Platforms: What Actually Works in GitHub, Notion, Obsidian, and Confluence&lt;/a&gt; — the broader platform-compatibility picture beyond callouts&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/why-your-markdown-isnt-rendering&quot;&gt;Why Your Markdown Isn&amp;#39;t Rendering: A Troubleshooting Guide&lt;/a&gt; — if a callout you pasted somewhere shows up as a plain blockquote instead of a styled box&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And if you&amp;#39;d rather generate the correct syntax for your target platform than memorize four different formats, try the &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-callouts&quot;&gt;Markdown Callout Generator&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Markdown Math Rendering Across Platforms: Where LaTeX Actually Works</title>
    <link href="https://www.markdowntools.com/blog/markdown-math-rendering-across-platforms" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/markdown-math-rendering-across-platforms</id>
    <published>2026-08-21T00:00:00+00:00</published>
    <updated>2026-08-21T00:00:00+00:00</updated>
    <author><name>Matt Rathbone</name></author>
    <category term="workflows"/>
    <summary>The $...$ / $$...$$ LaTeX convention for math in Markdown looks identical everywhere, but whether it renders at all — and which engine renders it — varies enormously across GitHub, GitLab, Obsidian, Notion, Jekyll, Hugo, Confluence, and Slack. Here's what's real.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/markdown-math-rendering-across-platforms">&lt;p&gt;Wrap a formula in dollar signs — &lt;code&gt;$E = mc^2$&lt;/code&gt; — and on some platforms you get a beautifully typeset equation. On others you get the literal text &lt;code&gt;$E = mc^2$&lt;/code&gt; sitting there unrendered, because the dollar-sign convention for math was never part of core Markdown, CommonMark, or even GitHub Flavored Markdown&amp;#39;s official spec. It&amp;#39;s a widely-adopted convention that individual platforms opt into (or don&amp;#39;t) on their own timeline, using different rendering engines with subtly different output. If you&amp;#39;ve ever pasted a formula that worked perfectly in one tool and showed up as raw LaTeX soup in another, this is why.&lt;/p&gt;
&lt;p&gt;This guide covers which platforms actually render math natively, which need a plugin or app, which don&amp;#39;t support it at all, and the practical differences between the two rendering engines you&amp;#39;ll run into everywhere: MathJax and KaTeX. If you want the LaTeX syntax basics themselves rather than platform behavior, our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-math-expressions-complete-guide&quot;&gt;complete guide to math expressions in Markdown&lt;/a&gt; covers that ground — and our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-math&quot;&gt;Markdown Math Preview tool&lt;/a&gt; lets you check a formula renders correctly, live in your browser, before you paste it anywhere.&lt;/p&gt;
&lt;h2 id=&quot;native-support-github-gitlab-obsidian&quot;&gt;Native Support: GitHub, GitLab, Obsidian&lt;/h2&gt;
&lt;p&gt;Three platforms render &lt;code&gt;$inline$&lt;/code&gt; and &lt;code&gt;$$block$$&lt;/code&gt; math out of the box, no setup required.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt; added native math rendering in 2022, using MathJax. It works in READMEs, issues, pull requests, wiki pages, and Markdown files viewed in the repo browser — anywhere GitHub renders Markdown. Inline math uses &lt;code&gt;$formula$&lt;/code&gt; with no space touching the dollar signs; block math uses &lt;code&gt;$$formula$$&lt;/code&gt; on its own line(s). GitHub&amp;#39;s implementation is deliberately conservative: it supports the common LaTeX commands (fractions, exponents, Greek letters, sums, integrals, matrices) but not obscure packages, and it silently falls back to plain text for anything it can&amp;#39;t parse rather than showing an error.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GitLab&lt;/strong&gt; has supported math rendering since GitLab 13.0 (2020), using KaTeX rather than MathJax. Syntax is the same &lt;code&gt;$...$&lt;/code&gt; / &lt;code&gt;$$...$$&lt;/code&gt; convention, and it works in issues, merge requests, wikis, and Markdown files. Because it&amp;#39;s KaTeX-based, GitLab&amp;#39;s rendering is closer to what our own Markdown Math Preview tool produces than GitHub&amp;#39;s MathJax output is — worth knowing if you&amp;#39;re maintaining docs that need to look consistent across both platforms.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Obsidian&lt;/strong&gt; renders math natively in both edit and reading views, using MathJax under the hood (via the KaTeX-compatible subset it bundles). This is genuinely useful for people keeping research notes or technical vaults, since the formula renders live as you type rather than requiring a separate preview step. Obsidian&amp;#39;s math also plays well with its block-reference and embed system, so you can reference a specific equation from another note.&lt;/p&gt;
&lt;h2 id=&quot;needs-a-plugin-static-site-generators&quot;&gt;Needs a Plugin: Static Site Generators&lt;/h2&gt;
&lt;p&gt;Static site generators almost universally treat &lt;code&gt;$&lt;/code&gt; as literal text by default. Math support has to be added deliberately.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Jekyll&lt;/strong&gt; (this blog&amp;#39;s own engine, and GitHub Pages&amp;#39; default) has no built-in math support in kramdown. You need a plugin like &lt;code&gt;jekyll-katex&lt;/code&gt;, or you can skip the plugin entirely and just load KaTeX&amp;#39;s or MathJax&amp;#39;s CSS/JS directly in your layout and let its auto-render script scan the page for &lt;code&gt;$...$&lt;/code&gt; after the page loads. The plugin approach pre-renders at build time (faster page loads, works without JavaScript); the client-side script approach is less setup but adds a render flash and a JS dependency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hugo&lt;/strong&gt; has first-class support for this through render hooks in newer versions (Hugo 0.122+), letting you wire up KaTeX or MathJax rendering for passthrough math blocks without a third-party module — Hugo&amp;#39;s Goldmark-based Markdown renderer can pass &lt;code&gt;$...$&lt;/code&gt;/&lt;code&gt;$$...$$&lt;/code&gt; through untouched for a script to pick up. Older Hugo setups typically use a shortcode or a theme-provided partial for the same effect. Either way, it&amp;#39;s opt-in configuration, not default behavior.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Docusaurus&lt;/strong&gt; supports math via two remark/rehype plugins — &lt;code&gt;remark-math&lt;/code&gt; and &lt;code&gt;rehype-katex&lt;/code&gt; — added to your &lt;code&gt;docusaurus.config.js&lt;/code&gt;. This is a well-documented, one-time setup, and once it&amp;#39;s in place, &lt;code&gt;$...$&lt;/code&gt;/&lt;code&gt;$$...$$&lt;/code&gt; in any &lt;code&gt;.md&lt;/code&gt;/&lt;code&gt;.mdx&lt;/code&gt; file just works. Because it&amp;#39;s explicitly KaTeX-based, output is consistent with GitLab&amp;#39;s and with our Markdown Math Preview tool.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MkDocs&lt;/strong&gt; similarly needs a math extension (&lt;code&gt;pymdownx.arithmatex&lt;/code&gt; from the PyMdown Extensions package is the common choice) plus a KaTeX or MathJax script reference in your theme, following the same &amp;quot;opt-in, one-time config&amp;quot; pattern as Hugo and Docusaurus.&lt;/p&gt;
&lt;h2 id=&quot;real-feature-different-trigger-notion&quot;&gt;Real Feature, Different Trigger: Notion&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Notion&lt;/strong&gt; supports LaTeX math, but not via the &lt;code&gt;$...$&lt;/code&gt; dollar-sign convention as your primary path — it&amp;#39;s a block/inline type you insert with the &lt;code&gt;/equation&lt;/code&gt; (or &lt;code&gt;/math&lt;/code&gt;) slash command, or by typing &lt;code&gt;$$&lt;/code&gt; and pressing space to trigger an inline equation editor. Once inserted, you type LaTeX inside that dedicated equation block, and Notion renders it with its own KaTeX-based engine. If you paste a Markdown file containing raw &lt;code&gt;$...$&lt;/code&gt; text into Notion, it generally will &lt;strong&gt;not&lt;/strong&gt; auto-convert that into a rendered equation the way headings or bold text get auto-converted on paste — you get literal dollar-sign text, and need to manually convert it via the equation block. This trips up anyone assuming Notion behaves like GitHub here; the feature exists, but the entry point is different.&lt;/p&gt;
&lt;h2 id=&quot;needs-a-marketplace-app-or-doesnt-support-it-confluence-jira-slack&quot;&gt;Needs a Marketplace App or Doesn&amp;#39;t Support It: Confluence, Jira, Slack&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Confluence&lt;/strong&gt; has no native &lt;code&gt;$...$&lt;/code&gt; math rendering in its standard editor. Cloud users can add a marketplace app (several LaTeX-rendering add-ons exist) that adds a macro for inserting rendered formulas, but that&amp;#39;s a dedicated macro/insert flow, not something that recognizes dollar-sign syntax typed inline. If you paste Markdown with LaTeX math into Confluence via the &lt;code&gt;/markdown&lt;/code&gt; panel (see our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-in-confluence-what-works&quot;&gt;Confluence guide&lt;/a&gt; for how that panel behaves generally), the math syntax passes through as literal text.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Jira&lt;/strong&gt; has no math rendering at all in its wiki markup or in the modern rich-text editor — there&amp;#39;s no macro, no plugin ecosystem equivalent to Confluence&amp;#39;s for this specific feature. If your team needs formulas in Jira tickets, the common workaround is rendering the equation elsewhere (including with our tool) and pasting it in as an image.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Slack&lt;/strong&gt; doesn&amp;#39;t support math rendering anywhere — not in the message composer, not via the API/Block Kit, not with a workaround short of posting a pre-rendered image. A message containing &lt;code&gt;$E = mc^2$&lt;/code&gt; displays as exactly that literal text. Given mrkdwn&amp;#39;s generally limited formatting (see our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-slack-formatting-guide&quot;&gt;Markdown to Slack guide&lt;/a&gt; for the full picture), this isn&amp;#39;t surprising, but it catches people off guard the first time.&lt;/p&gt;
&lt;h2 id=&quot;mathjax-vs-katex-why-the-same-formula-can-look-different&quot;&gt;MathJax vs. KaTeX: Why the Same Formula Can Look Different&lt;/h2&gt;
&lt;p&gt;Two rendering engines account for nearly every platform above, and they&amp;#39;re not pixel-identical:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MathJax&lt;/strong&gt; is the older, more complete implementation — broader LaTeX command coverage, including several packages and less-common symbols, at the cost of being noticeably heavier and historically slower (modern versions have closed much of that gap, but it&amp;#39;s still the heavier of the two). GitHub and Obsidian use MathJax or a MathJax-derived engine.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;KaTeX&lt;/strong&gt; is faster (it renders synchronously, without MathJax&amp;#39;s original asynchronous typesetting pass) and covers the large majority of everyday math notation, but it deliberately supports a smaller command set — some advanced LaTeX packages used in academic papers will throw a parse error in KaTeX that a full MathJax/LaTeX setup would render fine. GitLab, Docusaurus, MkDocs (with the common config), Notion, and our own Markdown Math Preview tool are all KaTeX-based.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In practice this means a formula that renders cleanly on GitHub isn&amp;#39;t guaranteed to look byte-for-byte identical on GitLab, and a formula relying on an obscure package might work in one and throw an error in the other. For everyday formulas — fractions, exponents, Greek letters, sums, integrals, standard matrices — you won&amp;#39;t notice a difference. For anything exotic, test on your actual target platform rather than assuming.&lt;/p&gt;
&lt;h2 id=&quot;quick-reference&quot;&gt;Quick Reference&lt;/h2&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Math support&lt;/th&gt;
&lt;th&gt;Engine&lt;/th&gt;
&lt;th&gt;How it&amp;#39;s triggered&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;GitHub&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;td&gt;MathJax&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$...$&lt;/code&gt; / &lt;code&gt;$$...$$&lt;/code&gt; anywhere Markdown renders&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitLab&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;td&gt;KaTeX&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$...$&lt;/code&gt; / &lt;code&gt;$$...$$&lt;/code&gt; anywhere Markdown renders&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Obsidian&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;td&gt;MathJax-derived&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$...$&lt;/code&gt; / &lt;code&gt;$$...$$&lt;/code&gt;, live in edit and reading views&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notion&lt;/td&gt;
&lt;td&gt;Real feature, different entry point&lt;/td&gt;
&lt;td&gt;KaTeX&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/equation&lt;/code&gt; block or &lt;code&gt;$$&lt;/code&gt; + space, not passive &lt;code&gt;$...$&lt;/code&gt; paste&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jekyll / GitHub Pages&lt;/td&gt;
&lt;td&gt;Needs plugin or manual script&lt;/td&gt;
&lt;td&gt;Your choice (KaTeX or MathJax)&lt;/td&gt;
&lt;td&gt;Plugin (e.g. &lt;code&gt;jekyll-katex&lt;/code&gt;) or client-side auto-render script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hugo&lt;/td&gt;
&lt;td&gt;Needs render hook or theme support&lt;/td&gt;
&lt;td&gt;Your choice&lt;/td&gt;
&lt;td&gt;Goldmark passthrough + KaTeX/MathJax script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docusaurus&lt;/td&gt;
&lt;td&gt;Needs plugin config&lt;/td&gt;
&lt;td&gt;KaTeX&lt;/td&gt;
&lt;td&gt;&lt;code&gt;remark-math&lt;/code&gt; + &lt;code&gt;rehype-katex&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MkDocs&lt;/td&gt;
&lt;td&gt;Needs extension&lt;/td&gt;
&lt;td&gt;Your choice&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pymdownx.arithmatex&lt;/code&gt; + theme script&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confluence&lt;/td&gt;
&lt;td&gt;Marketplace app only&lt;/td&gt;
&lt;td&gt;Varies by app&lt;/td&gt;
&lt;td&gt;Dedicated macro/insert, not inline &lt;code&gt;$...$&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jira&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;No native option; paste as image&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slack&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;No native option; paste as image&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;practical-takeaways&quot;&gt;Practical Takeaways&lt;/h2&gt;
&lt;p&gt;If you&amp;#39;re writing math-heavy documentation and want it to render without any setup on the reader&amp;#39;s end, GitHub, GitLab, or Obsidian are your only truly zero-configuration options. If your docs live on a static site generator, budget time for the one-time plugin setup — it&amp;#39;s usually a few lines of config, but it&amp;#39;s not automatic, and a Markdown file with unrendered &lt;code&gt;$...$&lt;/code&gt; littered through it looks broken to anyone who stumbles on the raw source before the setup is done. And if your formulas are headed to Confluence, Jira, or Slack, plan on images rather than fighting a platform that was never going to render LaTeX in the first place.&lt;/p&gt;
&lt;p&gt;Whichever platform you&amp;#39;re targeting, it&amp;#39;s worth checking the formula renders the way you expect before you commit it to a README or a ticket — a misplaced brace or an unsupported command shows up as a parse error or, worse, silently wrong output. Our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-math&quot;&gt;Markdown Math Preview tool&lt;/a&gt; renders both inline and block math live as you type, using KaTeX, and shows the exact parser error if something doesn&amp;#39;t parse — a fast sanity check before you paste a formula into any of the platforms above.&lt;/p&gt;
&lt;h2 id=&quot;related-reading&quot;&gt;Related Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-math-expressions-complete-guide&quot;&gt;Math Expressions in Markdown: Complete LaTeX and MathJax Guide&lt;/a&gt; — the LaTeX syntax fundamentals this post assumes you already know&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-platform-compatibility-guide&quot;&gt;Markdown Across Platforms: What Actually Works in GitHub, Notion, Obsidian, and Confluence&lt;/a&gt; — the broader platform-compatibility picture beyond just math&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-in-confluence-what-works&quot;&gt;Markdown in Confluence: What Actually Works&lt;/a&gt; — more detail on the &lt;code&gt;/markdown&lt;/code&gt; paste panel referenced above&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-slack-formatting-guide&quot;&gt;Markdown to Slack Formatting: What Actually Works&lt;/a&gt; — the full picture of mrkdwn&amp;#39;s formatting limits&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Markdown Task Lists and Checkboxes Across Platforms: What's Actually Interactive</title>
    <link href="https://www.markdowntools.com/blog/markdown-task-lists-checkboxes-across-platforms" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/markdown-task-lists-checkboxes-across-platforms</id>
    <published>2026-08-07T00:00:00+00:00</published>
    <updated>2026-08-07T00:00:00+00:00</updated>
    <author><name>Matt Rathbone</name></author>
    <category term="workflows"/>
    <summary>The `- [ ]` checkbox syntax looks the same everywhere, but whether it's actually clickable — and whether clicking it does anything — varies wildly across GitHub, GitLab, Confluence, Jira, Notion, Obsidian, and Linear. Here's what's real.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/markdown-task-lists-checkboxes-across-platforms">&lt;p&gt;Type &lt;code&gt;- [ ] Do the thing&lt;/code&gt; into a Markdown file and you&amp;#39;ll get a checkbox almost everywhere — GitHub, GitLab, Notion, Obsidian, and most static site generators all recognize the syntax. What&amp;#39;s inconsistent is what happens next. On some platforms that checkbox is genuinely clickable, and clicking it edits the real content. On others it renders as a checkbox-shaped image that does absolutely nothing when you click it — it&amp;#39;s just a disabled &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; tag with no logic wired up behind it. Confusing the two is an easy mistake to make, especially if your only experience is with the one platform where it happens to work.&lt;/p&gt;
&lt;p&gt;This guide covers where checkboxes are real, where they&amp;#39;re decoration, and the couple of places task lists silently break outright — plus a mention of our new &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-checklist&quot;&gt;Markdown Checklist Generator&lt;/a&gt; if you just want clean, correctly-nested checklist syntax without hand-typing brackets and indentation. If you&amp;#39;re looking for basic task list syntax itself rather than platform behavior, our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-task-lists-and-checkboxes-complete-guide&quot;&gt;complete guide to task lists and checkboxes&lt;/a&gt; covers that ground.&lt;/p&gt;
&lt;h2 id=&quot;github-interactive-in-issues-and-prs-static-in-files&quot;&gt;GitHub: Interactive in Issues and PRs, Static in Files&lt;/h2&gt;
&lt;p&gt;GitHub is the platform most people picture when they think of &amp;quot;clickable Markdown checkboxes,&amp;quot; and it&amp;#39;s also the platform with the split behavior that trips people up.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In issue bodies, pull request descriptions, and comments&lt;/strong&gt;, a &lt;code&gt;- [ ]&lt;/code&gt; checkbox renders as a real, clickable &lt;code&gt;&amp;lt;input type=&amp;quot;checkbox&amp;quot;&amp;gt;&lt;/code&gt; wired directly to the underlying issue/PR text. Click it (if you have write access, or you&amp;#39;re the author) and GitHub edits the stored Markdown body for you, flipping &lt;code&gt;[ ]&lt;/code&gt; to &lt;code&gt;[x]&lt;/code&gt; in place. This is genuinely interactive — the checkbox state and the raw text are the same source of truth, always in sync.&lt;/p&gt;
&lt;p&gt;GitHub also does something useful with this automatically: any issue or PR containing a task list gets an &amp;quot;N of M tasks complete&amp;quot; progress count that shows up in the issue list, the sidebar, and anywhere that issue is referenced or linked from another issue. You don&amp;#39;t have to configure anything for this — it&amp;#39;s computed straight from the checkbox count in the body text.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In regular repository files&lt;/strong&gt; — a README, a docs page, any &lt;code&gt;.md&lt;/code&gt; file rendered on github.com&amp;#39;s file browser — the story is different. GitHub still renders the checkbox visually, but it&amp;#39;s a disabled input with no click handler behind it, because there&amp;#39;s no &amp;quot;body&amp;quot; concept to persistently update; it&amp;#39;s just a static file being rendered as HTML. You can look at a checked-off roadmap in a README and it looks exactly like an interactive one, but nothing happens if you click it. This is the single most common source of confusion in this whole topic — people assume README checkboxes work like issue checkboxes because they&amp;#39;re visually identical.&lt;/p&gt;
&lt;h2 id=&quot;gitlab-the-same-split-as-github&quot;&gt;GitLab: The Same Split as GitHub&lt;/h2&gt;
&lt;p&gt;GitLab&amp;#39;s behavior mirrors GitHub closely enough that if you understand one, you understand the other. Task lists inside &lt;strong&gt;issue descriptions and merge request descriptions&lt;/strong&gt; are interactive — click a checkbox and GitLab persists the change to the description text, and issue lists show a task-completion count the same way GitHub&amp;#39;s does.&lt;/p&gt;
&lt;p&gt;Task lists rendered inside &lt;strong&gt;wiki pages and regular repository files&lt;/strong&gt; (README, any Markdown file in the file browser) are static, same as GitHub — visually identical checkboxes, no click behavior. If you&amp;#39;re building internal documentation in a GitLab wiki and picturing an interactive checklist your team can tick off together, that&amp;#39;s not what you&amp;#39;ll get; you&amp;#39;d want an actual issue or a dedicated task-tracking feature instead.&lt;/p&gt;
&lt;h2 id=&quot;confluence-a-real-interactive-feature-but-its-not-the-markdown-syntax&quot;&gt;Confluence: A Real Interactive Feature, But It&amp;#39;s Not the Markdown Syntax&lt;/h2&gt;
&lt;p&gt;Confluence has genuinely interactive, assignable task items — but they come from a distinct editor feature, not from typing GFM-style &lt;code&gt;- [ ]&lt;/code&gt; syntax. In the Confluence editor, typing &lt;code&gt;[]&lt;/code&gt; followed by a space (or using the Insert menu) creates an &lt;strong&gt;Action Item&lt;/strong&gt;: a real interactive checkbox that can be assigned to a person, given a due date, and tracked in Confluence&amp;#39;s own Tasks report across the whole space. That&amp;#39;s a more capable feature than a plain Markdown checkbox in most other tools.&lt;/p&gt;
&lt;p&gt;Where it gets inconsistent is pasting &lt;em&gt;raw Markdown&lt;/em&gt; task list syntax into Confluence — through the &lt;code&gt;/markdown&lt;/code&gt; slash-command panel we&amp;#39;ve &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-in-confluence-what-works&quot;&gt;covered before&lt;/a&gt;. Our own testing there found &lt;code&gt;- [x]&lt;/code&gt; syntax converts unreliably: sometimes it becomes a real Action Item, sometimes it just renders as literal bracket text, depending on the Confluence version and editor mode. If checkbox interactivity matters, use Confluence&amp;#39;s native Action Item feature directly rather than relying on a Markdown paste to produce one.&lt;/p&gt;
&lt;h2 id=&quot;jira-depends-entirely-on-which-editor-and-project-type&quot;&gt;Jira: Depends Entirely on Which Editor and Project Type&lt;/h2&gt;
&lt;p&gt;Jira wiki markup — the plain-text format used by Server, Data Center, and classic (non-next-gen) Cloud projects — has &lt;strong&gt;no checkbox syntax at all&lt;/strong&gt;. Typing &lt;code&gt;- [ ] Task&lt;/code&gt; into a wiki-markup field just renders as a literal bullet point followed by the literal characters &lt;code&gt;[ ] Task&lt;/code&gt;; there&amp;#39;s nothing for the renderer to interpret, which is exactly what our own &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-jira&quot;&gt;Markdown to Jira converter&lt;/a&gt; produces when it hits checkbox syntax, since there&amp;#39;s no Jira wiki-markup equivalent to convert it to.&lt;/p&gt;
&lt;p&gt;The modern Jira Cloud rich-text editor (used in team-managed/next-gen projects) does have a real interactive Task list block, but you reach it through the editor&amp;#39;s Insert menu or its own autoformat shortcut, not by pasting GitHub-style Markdown. If your workflow involves generating Jira content programmatically, it&amp;#39;s worth checking which editor your specific project type actually uses before assuming checkbox syntax will do anything.&lt;/p&gt;
&lt;h2 id=&quot;notion-converts-to-a-real-block-not-rendered-markdown-text&quot;&gt;Notion: Converts to a Real Block, Not Rendered Markdown Text&lt;/h2&gt;
&lt;p&gt;Notion doesn&amp;#39;t really &amp;quot;render Markdown&amp;quot; the way a static site or a GitHub file does — when you type or paste &lt;code&gt;- [ ]&lt;/code&gt; syntax, Notion converts it on the spot into one of its own native To-do blocks, which is a first-class, always-interactive, always-clickable element in Notion&amp;#39;s block-based document model. There&amp;#39;s no static/interactive split here the way there is on GitHub or GitLab, because by the time you see a checkbox in Notion, it&amp;#39;s no longer &amp;quot;Markdown&amp;quot; at all — it&amp;#39;s a Notion block that happens to have originated from Markdown syntax. We cover Notion&amp;#39;s broader import behavior, including where this conversion does and doesn&amp;#39;t kick in cleanly, in our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-for-notion-what-actually-works&quot;&gt;Markdown for Notion guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;obsidian-interactive-and-writes-back-to-the-file&quot;&gt;Obsidian: Interactive and Writes Back to the File&lt;/h2&gt;
&lt;p&gt;Obsidian is the one place in this list where &amp;quot;interactive&amp;quot; means something slightly different and arguably more useful: clicking a checkbox in Reading view or Live Preview doesn&amp;#39;t just update some in-memory display state, it actually edits the &lt;code&gt;.md&lt;/code&gt; file on disk, flipping &lt;code&gt;[ ]&lt;/code&gt; to &lt;code&gt;[x]&lt;/code&gt; in the raw text. Since Obsidian vaults are just folders of plain Markdown files, that checkbox click is a real file write, not an API call against a hosted document like GitHub&amp;#39;s. This makes Obsidian genuinely good for checklist-style notes you expect to interact with over time, and it&amp;#39;s part of why the &lt;a href=&quot;https://www.markdowntools.com/blog/obsidian-plugins-for-developers&quot;&gt;Tasks community plugin&lt;/a&gt; is popular — it extends this native checkbox behavior with due dates, recurring tasks, and query-based task views across your whole vault.&lt;/p&gt;
&lt;h2 id=&quot;linear-live-markdown-shortcut-conversion&quot;&gt;Linear: Live Markdown-Shortcut Conversion&lt;/h2&gt;
&lt;p&gt;Linear&amp;#39;s issue description editor supports Markdown-style shortcuts as you type, and task list syntax is one of them — typing &lt;code&gt;- [ ]&lt;/code&gt; converts it live into an interactive checklist item within the issue, similar in spirit to Notion&amp;#39;s live-conversion behavior. As with Notion, once it&amp;#39;s converted it&amp;#39;s a native editor element rather than literal text sitting in a Markdown file, so there isn&amp;#39;t a &amp;quot;static file&amp;quot; version of this to worry about the way there is with GitHub or GitLab.&lt;/p&gt;
&lt;h2 id=&quot;quick-reference&quot;&gt;Quick Reference&lt;/h2&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Where checkboxes are interactive&lt;/th&gt;
&lt;th&gt;Where they&amp;#39;re static or unsupported&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Issues, PRs, comments (with auto task-count tracking)&lt;/td&gt;
&lt;td&gt;Repository files (READMEs, docs) rendered on github.com&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GitLab&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Issue descriptions, merge request descriptions&lt;/td&gt;
&lt;td&gt;Wiki pages, repository files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Confluence&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Native Action Items (typed via &lt;code&gt;[]&lt;/code&gt; or Insert menu)&lt;/td&gt;
&lt;td&gt;Raw Markdown &lt;code&gt;- [ ]&lt;/code&gt; pasted via the &lt;code&gt;/markdown&lt;/code&gt; panel — inconsistent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Jira&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cloud team-managed project rich-text editor&amp;#39;s Task list block&lt;/td&gt;
&lt;td&gt;Jira wiki markup (Server/Data Center/classic Cloud) — no checkbox syntax exists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Notion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Always — converts to a native To-do block on entry&lt;/td&gt;
&lt;td&gt;N/A — it&amp;#39;s no longer Markdown once converted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Obsidian&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reading view and Live Preview — writes back to the file&lt;/td&gt;
&lt;td&gt;N/A — same file either way&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Linear&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Issue description editor, live Markdown-shortcut conversion&lt;/td&gt;
&lt;td&gt;N/A — converts on entry&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;the-automation-angle-githubs-completion-tracking&quot;&gt;The Automation Angle: GitHub&amp;#39;s Completion Tracking&lt;/h2&gt;
&lt;p&gt;Worth calling out on its own, since it&amp;#39;s easy to miss: GitHub computes checklist completion automatically, with zero configuration. Any issue or PR body with task list syntax gets a live &amp;quot;3 of 7 tasks complete&amp;quot; count wherever that issue is displayed — the issue list, the sidebar of a linked PR, search results. If you reference another issue as a checklist item (&lt;code&gt;- [ ] #123&lt;/code&gt;), GitHub can track that as a linked sub-issue and roll its completion into the parent&amp;#39;s progress. This is genuinely useful for release checklists and multi-step feature tracking, and it&amp;#39;s specific to GitHub — GitLab shows a similar per-issue completion count, but the cross-issue linking behavior isn&amp;#39;t identical, so don&amp;#39;t assume feature parity if you&amp;#39;re moving a workflow between the two.&lt;/p&gt;
&lt;h2 id=&quot;where-task-lists-break-outright&quot;&gt;Where Task Lists Break Outright&lt;/h2&gt;
&lt;p&gt;Two failure modes show up regardless of platform, because they&amp;#39;re rooted in how Markdown parsers work rather than in any single renderer&amp;#39;s choices:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Task lists inside table cells don&amp;#39;t render as checkboxes.&lt;/strong&gt; GFM table cells are parsed as inline content, and a task list is a block-level list construct — parsers don&amp;#39;t recognize block elements inside a table cell, so &lt;code&gt;- [ ]&lt;/code&gt; typed into a pipe-table cell just shows up as literal text, brackets and all, on every platform we&amp;#39;ve tested. If you need a checklist-style status column in a table, use a plain character (✅/⬜, or a simple &amp;quot;Done&amp;quot;/&amp;quot;Pending&amp;quot; column) instead of task list syntax.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nested checkbox indentation is stricter than it looks.&lt;/strong&gt; GFM expects consistent indentation — typically two spaces — for a nested task item to be recognized as a child of the item above it. Get the indentation wrong (three spaces, a stray tab, inconsistent indentation between sibling items) and the parser either treats the &amp;quot;nested&amp;quot; item as a new top-level list item or, on some renderers, breaks the list into two separate lists entirely. On GitHub specifically, this also affects whether a nested item&amp;#39;s completion counts toward the parent&amp;#39;s tracked progress, so a subtly wrong indent isn&amp;#39;t just a cosmetic issue there.&lt;/p&gt;
&lt;p&gt;Hand-typing nested checklists with correct, consistent indentation is exactly the kind of small-but-annoying formatting task worth offloading — our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-checklist&quot;&gt;Markdown Checklist Generator&lt;/a&gt; builds the syntax (including nesting) for you, so the raw Markdown is correct before it ever reaches whichever platform you&amp;#39;re pasting it into.&lt;/p&gt;
&lt;h2 id=&quot;related-reading&quot;&gt;Related Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-task-lists-and-checkboxes-complete-guide&quot;&gt;Markdown Task Lists and Checkboxes: Complete Guide&lt;/a&gt; — the syntax fundamentals and platform-specific formatting variations this post assumes you already know&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-platform-compatibility-guide&quot;&gt;Markdown Across Platforms: What Actually Works in GitHub, Notion, Obsidian, and Confluence&lt;/a&gt; — the broader platform-compatibility picture beyond just task lists&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/why-your-markdown-isnt-rendering&quot;&gt;Why Your Markdown Isn&amp;#39;t Rendering: A Troubleshooting Guide&lt;/a&gt; — if a checklist you pasted somewhere isn&amp;#39;t behaving the way you expected&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-in-confluence-what-works&quot;&gt;Markdown in Confluence: What Actually Works&lt;/a&gt; — more detail on Confluence&amp;#39;s Action Item feature and &lt;code&gt;/markdown&lt;/code&gt; panel behavior&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-jira-wiki-markup-guide&quot;&gt;Markdown to Jira Wiki Markup: The Complete Conversion Guide&lt;/a&gt; — for the full Jira wiki-markup syntax picture&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And if you just want correctly-formatted checklist Markdown without hand-typing brackets and indentation, try the &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-checklist&quot;&gt;Markdown Checklist Generator&lt;/a&gt;.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Word to Markdown: Pandoc vs. Mammoth.js vs. Online Converters vs. Copy-Paste</title>
    <link href="https://www.markdowntools.com/blog/word-to-markdown-pandoc-vs-mammoth-vs-online-converters" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/word-to-markdown-pandoc-vs-mammoth-vs-online-converters</id>
    <published>2026-08-01T00:00:00+00:00</published>
    <updated>2026-08-01T00:00:00+00:00</updated>
    <author><name>Matt Rathbone</name></author>
    <category term="workflows"/>
    <summary>Four real ways to get a Word document into Markdown, and the honest tradeoffs between them: fidelity, images, tables, footnotes, batch conversion, and whether your document leaves your machine.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/word-to-markdown-pandoc-vs-mammoth-vs-online-converters">&lt;p&gt;If you&amp;#39;ve searched for &amp;quot;convert Word to Markdown,&amp;quot; you&amp;#39;ve probably landed on a tutorial that walks you through one specific tool — usually Pandoc — and calls it done. That&amp;#39;s fine if Pandoc is actually the right tool for what you&amp;#39;re doing, but it often isn&amp;#39;t. A one-off conversion of a single memo has completely different requirements than migrating two hundred archived Word docs into a docs-as-code repo, and &amp;quot;install a command-line tool&amp;quot; is a non-starter for a lot of the people who actually need this conversion done.&lt;/p&gt;
&lt;p&gt;This guide skips the single-tool tutorial and instead compares the four real approaches people use, with the tradeoffs that actually matter: how well formatting survives, what happens to images and footnotes, whether you can batch-process a folder of files, and — often overlooked — whether your document has to leave your machine to get converted at all.&lt;/p&gt;
&lt;h2 id=&quot;the-four-approaches&quot;&gt;The Four Approaches&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Pandoc&lt;/strong&gt; — a command-line, do-everything document converter&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mammoth.js&lt;/strong&gt; — a purpose-built library that specifically targets clean Word-to-HTML/Markdown output, not general document conversion&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Online converters&lt;/strong&gt; — upload-and-download web tools, usually running Pandoc or Mammoth on a server behind a simple UI&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Copy-paste&lt;/strong&gt; — pasting Word content directly into a Markdown-aware editor and manually fixing the result&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;pandoc&quot;&gt;Pandoc&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://pandoc.org/&quot;&gt;Pandoc&lt;/a&gt; is the Swiss Army knife of document conversion — it converts between dozens of formats, and Word-to-Markdown is one of its best-supported paths.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;pandoc document.docx -f docx -t markdown -o document.md
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Handles footnotes properly — Word footnotes become real Markdown footnote syntax (&lt;code&gt;[^1]&lt;/code&gt;), not inline text dumps&lt;/li&gt;
&lt;li&gt;Extracts embedded images to a folder automatically with &lt;code&gt;--extract-media=./media&lt;/code&gt;, and rewrites the image references to point at them&lt;/li&gt;
&lt;li&gt;Scriptable and batchable — trivial to write a loop that converts an entire folder of &lt;code&gt;.docx&lt;/code&gt; files in one pass, which matters if you&amp;#39;re migrating an archive rather than converting one document&lt;/li&gt;
&lt;li&gt;Multiple Markdown &amp;quot;flavors&amp;quot; as output targets — GitHub-Flavored Markdown (&lt;code&gt;-t gfm&lt;/code&gt;), CommonMark, or Pandoc&amp;#39;s own extended Markdown, depending on where the output is headed&lt;/li&gt;
&lt;li&gt;Free, open source, and runs entirely on your machine — nothing about your document touches a third party&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&amp;#39;s a command-line tool. Someone has to install it, and it&amp;#39;s not something you can hand to a coworker who just wants to drag a file onto a web page&lt;/li&gt;
&lt;li&gt;Table conversion is functional but not always pretty — complex tables (merged cells, nested tables) can come out structurally correct but visually messy, needing manual cleanup&lt;/li&gt;
&lt;li&gt;Comments and tracked changes are dropped entirely — Pandoc converts the current document state, not the review history&lt;/li&gt;
&lt;li&gt;Styling nuance (custom paragraph styles, specific font choices) is intentionally discarded, since Markdown has no equivalent — this is correct behavior for Pandoc&amp;#39;s philosophy, but surprises people expecting a style-preserving conversion&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pandoc is the right choice when you&amp;#39;re comfortable with a terminal, need to convert more than a handful of files, or are building conversion into an automated pipeline (a CI job that converts newly-uploaded Word docs into a docs repo, for instance).&lt;/p&gt;
&lt;h2 id=&quot;mammothjs&quot;&gt;Mammoth.js&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/mwilliamson/mammoth.js&quot;&gt;Mammoth.js&lt;/a&gt; takes a narrower, more opinionated approach than Pandoc. It&amp;#39;s built specifically to convert &lt;code&gt;.docx&lt;/code&gt; to clean HTML (and, via a Markdown wrapper, to Markdown) by mapping Word&amp;#39;s &lt;em&gt;semantic&lt;/em&gt; styles — &amp;quot;Heading 1,&amp;quot; &amp;quot;Heading 2,&amp;quot; &amp;quot;Quote&amp;quot; — onto their HTML/Markdown equivalents, and deliberately ignoring Word&amp;#39;s more chaotic direct-formatting details (font sizes, exact colors, manual indentation) that don&amp;#39;t have a clean semantic mapping.&lt;/p&gt;
&lt;p&gt;This design choice is exactly why Mammoth tends to produce noticeably cleaner Markdown than a general-purpose converter for real-world business documents: a Word doc exported from a corporate template, full of inconsistent manual formatting layered on top of the actual paragraph styles, converts into simple, readable Markdown instead of dragging all that formatting noise along with it. The tradeoff is the same decision in reverse — if a document relies on manual formatting rather than proper Word styles (someone selected text and clicked &amp;quot;bold&amp;quot; and &amp;quot;18pt&amp;quot; instead of applying a Heading style), Mammoth has less to work with and the structural mapping is weaker.&lt;/p&gt;
&lt;p&gt;Mammoth is a JavaScript library, not a standalone CLI tool by default, which makes it the natural choice for two different audiences:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Developers embedding conversion in an app&lt;/strong&gt; — Mammoth runs both server-side (Node.js) and entirely client-side in a browser via WebAssembly-free pure JS, which is what makes browser-based, no-upload converters possible&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Anyone who wants a private, in-browser conversion&lt;/strong&gt; without installing anything or sending a file to a server&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Produces cleaner output than Pandoc specifically for documents built on proper Word styles, since it maps semantic structure rather than trying to preserve everything&lt;/li&gt;
&lt;li&gt;Runs entirely client-side in a browser — no upload, no server round-trip, which matters if the document is sensitive&lt;/li&gt;
&lt;li&gt;Lightweight and embeddable — a few lines of JavaScript, not a system dependency&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Narrower feature set than Pandoc — no footnote conversion, weaker table handling for complex/merged cells, and it&amp;#39;s Word-to-HTML/Markdown only (not the dozens of format pairs Pandoc supports)&lt;/li&gt;
&lt;li&gt;Quality depends heavily on the source document using real Word styles rather than manual formatting — a messily-formatted document won&amp;#39;t convert as cleanly as it would with a converter that tries to preserve everything literally&lt;/li&gt;
&lt;li&gt;Being a library rather than a CLI, it&amp;#39;s not something you&amp;#39;d reach for from a terminal for a quick one-off unless you&amp;#39;re comfortable writing a few lines of Node&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;online-converters&quot;&gt;Online Converters&lt;/h2&gt;
&lt;p&gt;Web-based upload-and-download tools are, under the hood, usually running either Pandoc or Mammoth on a server — the tool itself isn&amp;#39;t a third conversion engine so much as a UI wrapped around one of the first two. The real distinguishing factor between online converters isn&amp;#39;t conversion quality (that&amp;#39;s inherited from whichever engine is underneath), it&amp;#39;s &lt;strong&gt;where your document goes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This is the tradeoff most tutorials skip past, and it&amp;#39;s worth being direct about: uploading a document to a random website means that document — which might be a contract, an internal memo, an unpublished manuscript, anything — is sitting on someone else&amp;#39;s server, at least temporarily, possibly logged, possibly retained. For a public blog post you&amp;#39;re about to publish anyway, that&amp;#39;s a non-issue. For anything with confidential, personal, or legally sensitive content, it&amp;#39;s worth reading the site&amp;#39;s privacy policy before uploading, or better, using a tool where the conversion happens in your own browser and the file never leaves your machine at all.&lt;/p&gt;
&lt;p&gt;Our own &lt;a href=&quot;https://www.markdowntools.com/tools/word-to-markdown&quot;&gt;Word to Markdown tool&lt;/a&gt; is built specifically to avoid that tradeoff: it uses Mammoth.js and Turndown running client-side, in your browser, so the &lt;code&gt;.docx&lt;/code&gt; file you drop in is never uploaded anywhere — the conversion happens locally and the file never leaves your machine. If you&amp;#39;d rather do this conversion instantly in your browser without installing anything or worrying about where your document ends up, that&amp;#39;s what it&amp;#39;s for. It inherits Mammoth&amp;#39;s strengths (clean output for properly-styled documents, in-browser privacy) and its limitations (weaker on documents that rely on manual formatting rather than Word styles, and not the tool to reach for if you need Pandoc-grade footnote handling or a scripted batch pipeline).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Strengths (as a category):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Zero installation — genuinely the fastest path for a non-technical user or a single one-off conversion&lt;/li&gt;
&lt;li&gt;Client-side tools in this category (like ours) offer the privacy benefit of local-only processing, with no upload at all&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Weaknesses (as a category):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Server-side converters mean your document is uploaded somewhere — check what that site actually does with it&lt;/li&gt;
&lt;li&gt;Usually a single-file workflow — not built for batch-converting a folder of documents&lt;/li&gt;
&lt;li&gt;Feature set is whatever the underlying engine (Pandoc or Mammoth) supports, often with a subset of the command-line flags/options exposed through the UI&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;copy-paste&quot;&gt;Copy-Paste&lt;/h2&gt;
&lt;p&gt;The oldest method, and still a reasonable choice for a short, simple document: open the Word file, select the content, paste it into a Markdown-aware editor, and manually clean up the result.&lt;/p&gt;
&lt;p&gt;Some Markdown editors and note apps (Obsidian, Notion, several browser-based Markdown editors) will auto-detect basic formatting on paste — bold and italic survive, headings sometimes survive if they were built from Word&amp;#39;s actual Heading styles, plain paragraph text comes through fine. Tables, footnotes, and anything beyond basic inline formatting generally do not survive a raw paste and need to be rebuilt by hand.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No tooling at all beyond whatever editor you already use&lt;/li&gt;
&lt;li&gt;For a two-paragraph memo with a couple of bold words, genuinely faster than installing or uploading anything&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Doesn&amp;#39;t scale past a short document — the more structure (tables, lists, headings, footnotes) the source document has, the more manual rebuilding you&amp;#39;re signing up for&lt;/li&gt;
&lt;li&gt;Inconsistent behavior between target editors — what survives paste in Obsidian is not the same as what survives paste in a plain textarea&lt;/li&gt;
&lt;li&gt;No image handling at all — images from the Word doc don&amp;#39;t come along in a text paste; you&amp;#39;ll be exporting and re-inserting them separately&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Reasonable for a single short document with light formatting. Not a real strategy for anything longer or more structured.&lt;/p&gt;
&lt;h2 id=&quot;quick-reference&quot;&gt;Quick Reference&lt;/h2&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Fidelity&lt;/th&gt;
&lt;th&gt;Images&lt;/th&gt;
&lt;th&gt;Footnotes&lt;/th&gt;
&lt;th&gt;Batch conversion&lt;/th&gt;
&lt;th&gt;Privacy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pandoc&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High, especially footnotes/tables&lt;/td&gt;
&lt;td&gt;Extracted to a folder automatically&lt;/td&gt;
&lt;td&gt;Converts cleanly&lt;/td&gt;
&lt;td&gt;Yes — scriptable&lt;/td&gt;
&lt;td&gt;Local only, no upload&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mammoth.js&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High for style-based docs, lower for manually-formatted ones&lt;/td&gt;
&lt;td&gt;Extracted (base64 or file, depending on config)&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;Possible with custom scripting&lt;/td&gt;
&lt;td&gt;Local only if run client-side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Online converters&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Depends on underlying engine (Pandoc or Mammoth)&lt;/td&gt;
&lt;td&gt;Usually supported&lt;/td&gt;
&lt;td&gt;Depends on engine&lt;/td&gt;
&lt;td&gt;Rare — mostly single-file&lt;/td&gt;
&lt;td&gt;Server-side unless explicitly client-side (check the tool)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Copy-paste&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low — basic inline formatting only&lt;/td&gt;
&lt;td&gt;Not handled at all&lt;/td&gt;
&lt;td&gt;Not handled&lt;/td&gt;
&lt;td&gt;No — one document at a time&lt;/td&gt;
&lt;td&gt;Local (nothing uploaded)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;which-one-should-you-actually-use&quot;&gt;Which One Should You Actually Use?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Migrating an archive of Word documents into a docs-as-code repo&lt;/strong&gt; — Pandoc, scripted in a loop, with &lt;code&gt;--extract-media&lt;/code&gt; for images. This is the only approach in the list built for real batch work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A developer who wants Word-to-Markdown built into an app or an internal tool&lt;/strong&gt; — Mammoth.js, either server-side in Node or bundled client-side, depending on whether the app should avoid uploads.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A one-off conversion of a single document, and you don&amp;#39;t want to install anything&lt;/strong&gt; — a client-side online converter, like our &lt;a href=&quot;https://www.markdowntools.com/tools/word-to-markdown&quot;&gt;Word to Markdown tool&lt;/a&gt;, especially if the document has any sensitivity to it and you&amp;#39;d rather it never leave your browser.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A short memo with light formatting and you already have a Markdown editor open&lt;/strong&gt; — copy-paste is genuinely fine; don&amp;#39;t reach for a conversion tool to solve a problem that&amp;#39;s faster to fix by hand.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of these is universally &amp;quot;best&amp;quot; — they&amp;#39;re optimized for different situations, and the honest answer to &amp;quot;which Word to Markdown tool should I use&amp;quot; depends more on your document count, your formatting complexity, and your privacy requirements than on any single feature comparison.&lt;/p&gt;
&lt;h2 id=&quot;related-reading&quot;&gt;Related Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/how-to-convert-word-documents-to-markdown&quot;&gt;How to Convert Word Documents to Markdown: Complete Guide&lt;/a&gt; — the original step-by-step tutorial covering Pandoc installation and manual conversion techniques in more depth&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-word-docx-what-converts-cleanly&quot;&gt;Markdown to Word (DOCX): What Converts Cleanly and What Doesn&amp;#39;t&lt;/a&gt; — the reverse direction, for teams going from Markdown back to Word&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-platform-compatibility-guide&quot;&gt;Markdown Across Platforms: What Actually Works in GitHub, Notion, Obsidian, and Confluence&lt;/a&gt; — for what happens to your converted Markdown once it lands somewhere else&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/why-your-markdown-isnt-rendering&quot;&gt;Why Your Markdown Isn&amp;#39;t Rendering: A Troubleshooting Guide&lt;/a&gt; — if your converted Markdown doesn&amp;#39;t render the way you expect afterward&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And if you just want to convert a document right now without installing anything, try the &lt;a href=&quot;https://www.markdowntools.com/tools/word-to-markdown&quot;&gt;Word to Markdown converter&lt;/a&gt; — drop in a &lt;code&gt;.docx&lt;/code&gt; file, get clean Markdown out, entirely in your browser.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Markdown to Email HTML: Getting Your Content Into Newsletters and ESPs</title>
    <link href="https://www.markdowntools.com/blog/markdown-to-email-html-newsletters-and-esps" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/markdown-to-email-html-newsletters-and-esps</id>
    <published>2026-07-24T00:00:00+00:00</published>
    <updated>2026-07-24T00:00:00+00:00</updated>
    <author><name>Matt Rathbone</name></author>
    <category term="workflows"/>
    <summary>Email clients don't render HTML like browsers do — no &lt;style&gt; blocks, no external CSS, and Outlook uses Word's rendering engine. Here's what actually works when converting Markdown to email-ready HTML.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/markdown-to-email-html-newsletters-and-esps">&lt;p&gt;If you write your newsletter or transactional emails in Markdown and then paste the rendered HTML straight into your ESP (Mailchimp, Substack&amp;#39;s HTML block, SendGrid, a custom mailer), you&amp;#39;ve probably noticed it doesn&amp;#39;t look the way it did in your browser preview. Headings lose their styling, spacing collapses, and in Outlook things can look outright broken. That&amp;#39;s not a bug in your Markdown renderer — it&amp;#39;s because HTML email is not the same target as a web page, and a plain &lt;code&gt;markdown-to-html&lt;/code&gt; conversion doesn&amp;#39;t account for the difference.&lt;/p&gt;
&lt;p&gt;This guide covers why email HTML has to be built differently, what converts cleanly, the gotchas that catch people off guard, and how our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-email-html&quot;&gt;Markdown to Email HTML converter&lt;/a&gt; handles it.&lt;/p&gt;
&lt;h2 id=&quot;why-email-html-is-a-different-target&quot;&gt;Why Email HTML Is a Different Target&lt;/h2&gt;
&lt;p&gt;A normal web page loads a stylesheet once and applies it to every element on the page. Email doesn&amp;#39;t work that way, for a simple reason: most email clients strip &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks and ignore external CSS entirely, either for security or because their rendering engine doesn&amp;#39;t support it. Gmail, in particular, is aggressive about stripping &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; styles in some contexts. That means the only styling that reliably survives is a &lt;code&gt;style=&amp;quot;...&amp;quot;&lt;/code&gt; attribute written directly on the element itself — this is called &lt;strong&gt;inlining&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The second difference is layout. Browsers give you flexbox, grid, and reasonably consistent box-model behavior across engines. Email clients don&amp;#39;t. The safest way to guarantee a layout holds together across a dozen different rendering engines is still the decades-old technique of nesting &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; elements — not because tables are a good layout tool in 2026, but because table rendering is the one thing almost every email client agrees on.&lt;/p&gt;
&lt;p&gt;Put together, that means converting Markdown to email-safe HTML isn&amp;#39;t just &amp;quot;run it through a Markdown parser.&amp;quot; It means:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Every styled element gets its CSS written inline, on the tag itself&lt;/li&gt;
&lt;li&gt;The outer structure — the thing that keeps your content centered and a sane width — uses a table shell instead of &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s and CSS width rules&lt;/li&gt;
&lt;li&gt;You explicitly plan for what email clients don&amp;#39;t support (external images that aren&amp;#39;t hosted, &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks, modern CSS like flexbox)&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;the-design-choice-styled-semantic-tags-not-table-soup&quot;&gt;The Design Choice: Styled Semantic Tags, Not Table Soup&lt;/h2&gt;
&lt;p&gt;Some hand-coded email tools go further and convert every single text element — headings, paragraphs, list items — into nested &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;/&lt;code&gt;&amp;lt;td&amp;gt;&lt;/code&gt; structures, on the theory that tables are the only truly safe primitive. We didn&amp;#39;t do that in our converter, and it&amp;#39;s worth explaining why, since it&amp;#39;s a deliberate tradeoff rather than a shortcut.&lt;/p&gt;
&lt;p&gt;Our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-email-html&quot;&gt;Markdown to Email HTML tool&lt;/a&gt; uses real semantic tags — &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;–&lt;code&gt;&amp;lt;h6&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt;/&lt;code&gt;&amp;lt;ol&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;blockquote&amp;gt;&lt;/code&gt; — each with a full inline &lt;code&gt;style&lt;/code&gt; attribute, and reserves table markup for the two places where it actually matters: the outer structural shell (a single-column, centered, fixed-width wrapper) and any actual Markdown tables in your source content. Modern email clients (Gmail, Apple Mail, most mobile mail apps) render styled semantic HTML just fine. The clients that historically didn&amp;#39;t — older Outlook versions using Word&amp;#39;s rendering engine — have specific, narrower problems (mostly around margins, background images, and some CSS properties), not a blanket rejection of non-table markup. Table-izing every paragraph adds a lot of markup weight and makes the output harder to hand-edit, for a compatibility gain that mostly doesn&amp;#39;t apply anymore outside of layout-critical sections.&lt;/p&gt;
&lt;p&gt;If your use case is a highly stylized marketing email — multi-column layouts, background images behind text, precise pixel positioning — you&amp;#39;ll still want a dedicated tool like MJML (more on that below). This tool is aimed at the more common case: turning a piece of Markdown-authored content (a newsletter issue, a docs update, a transactional notice) into clean, reliable email HTML without hand-coding it.&lt;/p&gt;
&lt;h2 id=&quot;what-converts-cleanly&quot;&gt;What Converts Cleanly&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Headings&lt;/strong&gt; — converted to &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;–&lt;code&gt;&amp;lt;h6&amp;gt;&lt;/code&gt; with inline font-size, weight, and color, using an Arial/Helvetica fallback stack (email clients have unreliable web font support, so system fonts are the safe default)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bold, italic, strikethrough, inline code&lt;/strong&gt; — straightforward inline styling&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Paragraphs&lt;/strong&gt; — inline margin/line-height for readable spacing&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Links&lt;/strong&gt; — inline-styled &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tags, underlined, in a consistent brand color&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lists&lt;/strong&gt; — bulleted and numbered, including nesting&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blockquotes&lt;/strong&gt; — left border plus muted, italicized text&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fenced code blocks&lt;/strong&gt; — monospace font on a shaded background, with &lt;code&gt;white-space: pre-wrap&lt;/code&gt; instead of horizontal scrolling, since email clients handle scrollable regions inconsistently (some just clip the overflow)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tables&lt;/strong&gt; — inline-styled &lt;code&gt;&amp;lt;th&amp;gt;&lt;/code&gt;/&lt;code&gt;&amp;lt;td&amp;gt;&lt;/code&gt;, with column alignment preserved&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Horizontal rules&lt;/strong&gt; — a simple bordered &lt;code&gt;&amp;lt;hr&amp;gt;&lt;/code&gt;-equivalent&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Images&lt;/strong&gt; — &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags with inline &lt;code&gt;max-width: 100%&lt;/code&gt; so they scale on mobile&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;what-to-watch-out-for&quot;&gt;What to Watch Out For&lt;/h2&gt;
&lt;h3 id=&quot;images-need-to-be-hosted-somewhere-public&quot;&gt;Images need to be hosted somewhere public&lt;/h3&gt;
&lt;p&gt;This is the one that trips people up most. A Markdown image reference — whether it&amp;#39;s a relative path (&lt;code&gt;./chart.png&lt;/code&gt;), a local file, or embedded base64 data — needs to become an absolute URL pointing at a publicly reachable image host before it&amp;#39;ll actually display in an inbox. Most email clients either strip data URIs entirely or don&amp;#39;t resolve relative paths (there&amp;#39;s no concept of &amp;quot;relative to this file&amp;quot; once your HTML is sitting in someone&amp;#39;s inbox). Upload your images to your ESP&amp;#39;s asset host, an S3 bucket, or wherever your site serves static assets, then swap in the absolute URL. Our converter flags this with a warning banner rather than silently producing broken image tags.&lt;/p&gt;
&lt;h3 id=&quot;outlooks-rendering-engine-is-a-special-case&quot;&gt;Outlook&amp;#39;s rendering engine is a special case&lt;/h3&gt;
&lt;p&gt;Desktop Outlook (2007 through current versions, on Windows) doesn&amp;#39;t use a browser engine to render HTML email — it uses Microsoft Word&amp;#39;s layout engine. This is a long-standing, well-documented quirk of the email ecosystem, not something specific to any one converter. It means a handful of common CSS properties behave differently or are ignored outright: background images on tables often don&amp;#39;t render, &lt;code&gt;max-width&lt;/code&gt; is unreliable (which is part of why fixed pixel widths are still common in email templates), and margin/padding on certain elements can collapse unexpectedly. There&amp;#39;s no way for a client-side converter to fully paper over this — vendor-specific conditional comments (&lt;code&gt;&amp;lt;!--[if mso]--&amp;gt;&lt;/code&gt;) are the traditional workaround, and they&amp;#39;re a manual, Outlook-specific layer on top of otherwise-standard HTML. Don&amp;#39;t expect pixel-perfect Outlook rendering from any automated Markdown-to-email conversion, ours included; test in Outlook specifically if that audience matters to you.&lt;/p&gt;
&lt;h3 id=&quot;dark-mode-can-invert-your-colors-unpredictably&quot;&gt;Dark mode can invert your colors unpredictably&lt;/h3&gt;
&lt;p&gt;A growing share of mail clients now apply automatic dark-mode processing to emails that don&amp;#39;t explicitly declare their own dark-mode styles, and the results are inconsistent across clients — some invert colors, some leave images alone but darken backgrounds, some do nothing at all. If your palette depends on specific colors reading correctly (a light background behind dark text, for instance), it&amp;#39;s worth testing in at least one dark-mode client before sending to a large list. This is a known limitation of the underlying HTML email ecosystem, not something a single converter fixes for you.&lt;/p&gt;
&lt;h3 id=&quot;task-list-checkboxes-raw-html-mermaid-diagrams-and-footnotes&quot;&gt;Task list checkboxes, raw HTML, Mermaid diagrams, and footnotes&lt;/h3&gt;
&lt;p&gt;Following the same pattern as our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-for-notion-what-actually-works&quot;&gt;Notion&lt;/a&gt; and &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-slack-formatting-guide&quot;&gt;Slack&lt;/a&gt; converters, a few Markdown features don&amp;#39;t have a clean email equivalent:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Task list checkboxes&lt;/strong&gt; (&lt;code&gt;- [ ]&lt;/code&gt;) render as plain ☑/☐ text characters, not interactive form controls — HTML checkboxes inside email are unreliable at best and blocked at worst&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Raw HTML&lt;/strong&gt; passed through your Markdown source keeps its tags but doesn&amp;#39;t automatically get the inline-style treatment, since the converter only styles the elements it generates itself&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mermaid diagram fences&lt;/strong&gt; are kept as plain, unstyled code blocks — no email client renders Mermaid, or JavaScript of any kind&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Footnote references&lt;/strong&gt; (&lt;code&gt;[^1]&lt;/code&gt;) are left as literal text, since the parser doesn&amp;#39;t expand them&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Our tool shows a warning banner for each of these when it detects them in your source, the same pattern used across our other conversion tools.&lt;/p&gt;
&lt;h2 id=&quot;comparison-how-people-actually-solve-this&quot;&gt;Comparison: How People Actually Solve This&lt;/h2&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Tradeoff&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Markdown to Email HTML converter&lt;/strong&gt; (this tool)&lt;/td&gt;
&lt;td&gt;Turning existing Markdown content into inline-styled HTML fast, no signup&lt;/td&gt;
&lt;td&gt;Semantic-tag approach, not a full visual template builder&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MJML&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Building polished, multi-column marketing templates from scratch&lt;/td&gt;
&lt;td&gt;You write MJML markup, not Markdown — a different authoring workflow, with a build step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Word&amp;#39;s &amp;quot;paste from Markdown&amp;quot; / your ESP&amp;#39;s rich text editor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One-off emails where you&amp;#39;re comfortable manually formatting&lt;/td&gt;
&lt;td&gt;No inline-CSS control, styling is whatever the editor&amp;#39;s defaults are&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Litmus / Email on Acid&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pre-send rendering verification across dozens of real email clients&lt;/td&gt;
&lt;td&gt;Testing tools, not converters — you still need HTML to test&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hand-coding email HTML&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Maximum control, pixel-perfect Outlook support&lt;/td&gt;
&lt;td&gt;Slow, requires deep email-HTML expertise, easy to regress&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;For content that starts life as Markdown — a newsletter issue drafted the same way you&amp;#39;d draft any other doc, a changelog, a docs update you&amp;#39;re also emailing to subscribers — the converter approach is the fastest path from draft to send-ready HTML. If you&amp;#39;re building a highly designed, multi-column marketing template from scratch, MJML or a dedicated email design tool is still the better starting point. And regardless of which approach you use to generate the HTML, running it through Litmus or Email on Acid before a big send is the only real way to catch client-specific rendering surprises before your subscribers do.&lt;/p&gt;
&lt;h2 id=&quot;using-the-converter&quot;&gt;Using the Converter&lt;/h2&gt;
&lt;p&gt;Our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-email-html&quot;&gt;Markdown to Email HTML tool&lt;/a&gt; takes your Markdown on one side and gives you fully inline-styled HTML on the other, plus a rendered preview so you can sanity-check the result before you leave the page:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Paste Markdown in, get inline-styled HTML out — no &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks or CSS classes anywhere in the output&lt;/li&gt;
&lt;li&gt;Table-based structural shell (centered, 600px wide — the standard hand-coded email template width) around styled semantic content&lt;/li&gt;
&lt;li&gt;Copy HTML button for pasting straight into your ESP&amp;#39;s HTML block, plus a Download .html option&lt;/li&gt;
&lt;li&gt;Rendered visual preview so you can check the result without needing to send a test email first&lt;/li&gt;
&lt;li&gt;Warning banners for the specific gotchas above (image hosting, task-checkbox rendering, raw HTML, Mermaid, footnotes) so you know exactly what to double check before sending&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No signup, no upload — everything runs in your browser.&lt;/p&gt;
&lt;h2 id=&quot;related-posts-and-tools&quot;&gt;Related Posts and Tools&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-email-html&quot;&gt;Markdown to Email HTML converter&lt;/a&gt; — the tool this post covers&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-platform-compatibility-guide&quot;&gt;Markdown Across Platforms&lt;/a&gt; — how Markdown behaves across GitHub, Notion, Obsidian, and Confluence&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/why-your-markdown-isnt-rendering&quot;&gt;Why Your Markdown Isn&amp;#39;t Rendering&lt;/a&gt; — troubleshooting guide covering renderer-specific quirks like the ones in this post&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-word-docx-what-converts-cleanly&quot;&gt;Markdown to Word (DOCX): What Converts Cleanly and What Doesn&amp;#39;t&lt;/a&gt; — the same &amp;quot;what survives conversion&amp;quot; framing, for a different output format&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Markdown Style Guide: Common Mistakes and How to Catch Them</title>
    <link href="https://www.markdowntools.com/blog/markdown-style-guide-common-mistakes" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/markdown-style-guide-common-mistakes</id>
    <published>2026-07-17T00:00:00+00:00</published>
    <updated>2026-07-17T00:00:00+00:00</updated>
    <author><name>Matt Rathbone</name></author>
    <category term="workflows"/>
    <summary>The 10 most common Markdown style mistakes — broken heading hierarchy, missing alt text, bare URLs, and more — with before/after examples and how to catch them before you publish.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/markdown-style-guide-common-mistakes">&lt;p&gt;Markdown is forgiving. Almost anything you type will render into &lt;em&gt;something&lt;/em&gt; — which is exactly the problem. A skipped heading level, an image with no alt text, or a URL you forgot to wrap in link syntax will all render &amp;quot;fine&amp;quot; in your editor&amp;#39;s preview, then quietly cause real problems once the document ships: broken screen-reader navigation, a table of contents that doesn&amp;#39;t nest correctly, a link that looks wrong on a different platform.&lt;/p&gt;
&lt;p&gt;None of these are syntax errors. Markdown doesn&amp;#39;t have a compiler that stops you. That&amp;#39;s exactly why linting matters — and why we built a &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-linter&quot;&gt;Markdown Linter&lt;/a&gt; that checks for these issues in the browser, no setup required.&lt;/p&gt;
&lt;p&gt;This post walks through the mistakes that actually show up in real documents, grouped by why they matter, with a before/after for each.&lt;/p&gt;
&lt;h2 id=&quot;why-markdown-style-mistakes-matter&quot;&gt;Why Markdown Style Mistakes Matter&lt;/h2&gt;
&lt;p&gt;Unlike a programming language, Markdown failures are usually silent. Three categories of consequence show up over and over:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Accessibility&lt;/strong&gt; — screen readers rely on a logical heading structure and on alt text to describe images. Get either wrong, and you&amp;#39;ve made the document harder to use for anyone relying on assistive technology.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Structure and navigation&lt;/strong&gt; — table-of-contents generators, doc-site sidebars, and &amp;quot;on this page&amp;quot; widgets all build their outline from your headings. A skipped level (H2 straight to H4) breaks that outline even though the page still looks normal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-renderer consistency&lt;/strong&gt; — a bare URL, a code block missing its language tag, or trailing whitespace can render differently — or not at all — depending on whether the renderer is GitHub, a static site generator, or an internal wiki.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;None of these show up as a red squiggly line while you&amp;#39;re typing. You only find out when the rendered page looks wrong, or an accessibility audit flags it, or someone tells you the sidebar navigation skipped a section.&lt;/p&gt;
&lt;h2 id=&quot;the-10-rules-with-beforeafter-examples&quot;&gt;The 10 Rules, With Before/After Examples&lt;/h2&gt;
&lt;p&gt;Our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-linter&quot;&gt;Markdown Linter&lt;/a&gt; checks for 10 specific issues, split into one hard error and nine warnings. Here&amp;#39;s what each one actually catches.&lt;/p&gt;
&lt;h3 id=&quot;1-heading-hierarchy-error&quot;&gt;1. Heading hierarchy (error)&lt;/h3&gt;
&lt;p&gt;The only rule in the tool that&amp;#39;s treated as an &lt;strong&gt;error&lt;/strong&gt; rather than a warning, because it&amp;#39;s the one most likely to break something structural, not just look untidy.&lt;/p&gt;
&lt;p&gt;Headings should step down one level at a time. You can jump back &lt;em&gt;up&lt;/em&gt; freely (H3 back to H2 is fine — that&amp;#39;s just starting a new section), but you should never skip a level going &lt;em&gt;down&lt;/em&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&amp;lt;!-- Bad: skips H2 entirely --&amp;gt;
&lt;span class=&quot;hljs-section&quot;&gt;# Getting Started&lt;/span&gt;
&lt;span class=&quot;hljs-section&quot;&gt;### Installation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&amp;lt;!-- Good --&amp;gt;
&lt;span class=&quot;hljs-section&quot;&gt;# Getting Started&lt;/span&gt;
&lt;span class=&quot;hljs-section&quot;&gt;## Installation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Why it matters beyond looks: screen readers let users jump between headings by level, and they announce the jump (&amp;quot;entering level 3 heading&amp;quot;) — a skipped level is confusing out of context. Search engines and TOC generators use the same hierarchy to build page outlines, so a skip can produce a lopsided or broken outline even though the text itself reads fine.&lt;/p&gt;
&lt;h3 id=&quot;2-multiple-h1s&quot;&gt;2. Multiple H1s&lt;/h3&gt;
&lt;p&gt;A document should have exactly one H1 — the page title. A second H1 partway through usually means someone meant to use H2.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&amp;lt;!-- Bad --&amp;gt;
&lt;span class=&quot;hljs-section&quot;&gt;# My Guide&lt;/span&gt;
Some intro text.
&lt;span class=&quot;hljs-section&quot;&gt;# Advanced Topics&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&amp;lt;!-- Good --&amp;gt;
&lt;span class=&quot;hljs-section&quot;&gt;# My Guide&lt;/span&gt;
Some intro text.
&lt;span class=&quot;hljs-section&quot;&gt;## Advanced Topics&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;3-duplicate-headings&quot;&gt;3. Duplicate headings&lt;/h3&gt;
&lt;p&gt;The same heading text appearing twice in one document — often from copy-pasting a section as a template for a new one and forgetting to rename it.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&amp;lt;!-- Bad --&amp;gt;
&lt;span class=&quot;hljs-section&quot;&gt;## Configuration&lt;/span&gt;
...
&lt;span class=&quot;hljs-section&quot;&gt;## Configuration&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This matters for anchor links specifically: most renderers generate a heading&amp;#39;s URL fragment (&lt;code&gt;#configuration&lt;/code&gt;) from its text, so the second &amp;quot;Configuration&amp;quot; heading either silently gets a suffixed slug (&lt;code&gt;#configuration-1&lt;/code&gt;) that nobody linked to on purpose, or overwrites the first one&amp;#39;s anchor depending on the renderer — either way, a link meant for the first section can land on the wrong one.&lt;/p&gt;
&lt;h3 id=&quot;4-no-language-on-fenced-code-blocks&quot;&gt;4. No language on fenced code blocks&lt;/h3&gt;
&lt;p&gt;A &lt;code&gt;```&lt;/code&gt; fence with no language identifier after it loses syntax highlighting and, on some platforms, loses the copy-button / line-numbering features that are tied to language detection.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&amp;lt;!-- Bad --&amp;gt;
&lt;span class=&quot;hljs-code&quot;&gt;```
function greet(name) {
  return `Hello, ${name}`;
}
```&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&amp;lt;!-- Good --&amp;gt;
&lt;span class=&quot;hljs-code&quot;&gt;```javascript
function greet(name) {
  return `Hello, ${name}`;
}
```&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;5-bare-urls&quot;&gt;5. Bare URLs&lt;/h3&gt;
&lt;p&gt;A plain URL sitting in text, not wrapped in &lt;code&gt;[text](url)&lt;/code&gt; link syntax.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&amp;lt;!-- Bad --&amp;gt;
See https://example.com/docs/api-reference for details.
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&amp;lt;!-- Good --&amp;gt;
See the [&lt;span class=&quot;hljs-string&quot;&gt;API reference&lt;/span&gt;](&lt;span class=&quot;hljs-link&quot;&gt;https://example.com/docs/api-reference&lt;/span&gt;) for details.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Some renderers auto-linkify bare URLs (GitHub does); others don&amp;#39;t, and you end up with dead-looking plain text. Even where auto-linking works, a long raw URL is worse for readability and worse for screen readers, which read out the entire URL character by character instead of a meaningful label.&lt;/p&gt;
&lt;h3 id=&quot;6-missing-alt-text&quot;&gt;6. Missing alt text&lt;/h3&gt;
&lt;p&gt;An image reference with empty alt text — &lt;code&gt;![]()&lt;/code&gt; instead of &lt;code&gt;![description](...)&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&amp;lt;!-- Bad --&amp;gt;
![](&lt;span class=&quot;hljs-link&quot;&gt;chart.png&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&amp;lt;!-- Good --&amp;gt;
![&lt;span class=&quot;hljs-string&quot;&gt;Bar chart showing monthly signups from January to June&lt;/span&gt;](&lt;span class=&quot;hljs-link&quot;&gt;chart.png&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is the single highest-impact accessibility fix on this list. Screen reader users get nothing at all from an image with empty alt text — not even a filename fallback in most cases. It&amp;#39;s also one of the easiest mistakes to make, since a missing alt attribute doesn&amp;#39;t change how the image looks to a sighted reader.&lt;/p&gt;
&lt;h3 id=&quot;7-hard-tabs-for-indentation&quot;&gt;7. Hard tabs for indentation&lt;/h3&gt;
&lt;p&gt;A line indented with a literal tab character instead of spaces.&lt;/p&gt;
&lt;p&gt;Hard tabs render inconsistently — different editors and renderers expand a tab to a different number of spaces (commonly 4 or 8), so indentation-sensitive content like nested lists can shift alignment depending on where it&amp;#39;s viewed. Most Markdown style guides (including markdownlint&amp;#39;s defaults) require spaces for exactly this reason.&lt;/p&gt;
&lt;h3 id=&quot;8-trailing-whitespace&quot;&gt;8. Trailing whitespace&lt;/h3&gt;
&lt;p&gt;Whitespace at the end of a line. This is a genuinely dangerous one because Markdown gives &lt;em&gt;exactly two trailing spaces&lt;/em&gt; special meaning: it forces a line break within a paragraph. One trailing space (or three, or an odd number) does nothing visible in most editors, so it silently changes rendering behavior in a way you won&amp;#39;t notice until you inspect the raw output. (The linter is deliberately careful here — it only flags true trailing whitespace, not the intentional two-space line-break convention.)&lt;/p&gt;
&lt;h3 id=&quot;9-multiple-consecutive-blank-lines&quot;&gt;9. Multiple consecutive blank lines&lt;/h3&gt;
&lt;p&gt;More than one blank line between blocks. Markdown collapses any run of blank lines down to a single paragraph break during rendering, so extra blank lines have zero visual effect — they&amp;#39;re pure noise in the source that makes diffs longer and documents harder to scan in an editor.&lt;/p&gt;
&lt;h3 id=&quot;10-line-length&quot;&gt;10. Line length&lt;/h3&gt;
&lt;p&gt;A prose line longer than 120 characters. Code blocks are excluded from this check, since code often has legitimate reasons to run long (and it&amp;#39;s usually monospaced and horizontally scrollable in rendered output anyway).&lt;/p&gt;
&lt;p&gt;Long prose lines aren&amp;#39;t a rendering problem — Markdown reflows paragraphs regardless of source line length — but they&amp;#39;re a &lt;em&gt;diff&lt;/em&gt; problem. In a docs-as-code workflow where changes are reviewed as Git diffs, a one-word edit to a 400-character line shows the entire line as changed, making the actual edit hard to spot in review.&lt;/p&gt;
&lt;h2 id=&quot;in-browser-linting-vs-ci-pipeline-linting&quot;&gt;In-Browser Linting vs. CI Pipeline Linting&lt;/h2&gt;
&lt;p&gt;If you&amp;#39;ve read our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-for-technical-writers-docs-as-code&quot;&gt;docs-as-code workflow guide&lt;/a&gt;, you already know about &lt;a href=&quot;https://github.com/DavidAnson/markdownlint&quot;&gt;markdownlint&lt;/a&gt; and &lt;a href=&quot;https://vale.sh&quot;&gt;Vale&lt;/a&gt; — the standard tools for enforcing Markdown style in CI. Those are the right tools for a real docs pipeline: they run on every pull request, they&amp;#39;re configurable per-project, and Vale in particular goes beyond structure into prose style (banned phrases, terminology consistency, passive voice).&lt;/p&gt;
&lt;p&gt;Our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-linter&quot;&gt;Markdown Linter&lt;/a&gt; isn&amp;#39;t a replacement for that setup — it&amp;#39;s the fast check &lt;em&gt;before&lt;/em&gt; you get anywhere near a pull request. Paste a document, get instant feedback, no &lt;code&gt;npm install&lt;/code&gt;, no config file, no CI run to wait on. It&amp;#39;s useful in a few specific situations a CI pipeline doesn&amp;#39;t cover well:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reviewing someone else&amp;#39;s Markdown&lt;/strong&gt; you don&amp;#39;t have local tooling set up for — paste it and check in seconds&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Before pasting into a CMS or wiki&lt;/strong&gt; that doesn&amp;#39;t run any linting at all, where structural issues silently affect SEO and accessibility with no safety net&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quick self-review while drafting&lt;/strong&gt;, before you&amp;#39;ve even opened a terminal&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Teaching or onboarding&lt;/strong&gt; — showing someone exactly what &amp;quot;broken heading hierarchy&amp;quot; looks like, with immediate before/after feedback, rather than pointing them at a CI log&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Think of it as the same relationship a linter extension in your editor has to your project&amp;#39;s full CI test suite: faster feedback, narrower scope, and no substitute for the real pipeline once the stakes are high enough to need one.&lt;/p&gt;
&lt;h2 id=&quot;try-it&quot;&gt;Try It&lt;/h2&gt;
&lt;p&gt;Paste your Markdown into the &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-linter&quot;&gt;Markdown Linter&lt;/a&gt; and you&amp;#39;ll get line-numbered results, color-coded by severity, updating as you type. No signup, nothing leaves your browser.&lt;/p&gt;
&lt;h2 id=&quot;related-reading&quot;&gt;Related Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/markdown-linter&quot;&gt;Markdown Linter tool&lt;/a&gt; — check your Markdown for these 10 issues instantly&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-for-technical-writers-docs-as-code&quot;&gt;Markdown for Technical Writers: The Docs-as-Code Workflow Guide&lt;/a&gt; — markdownlint and Vale in a CI pipeline&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/why-your-markdown-isnt-rendering&quot;&gt;Why Your Markdown Isn&amp;#39;t Rendering&lt;/a&gt; — troubleshooting guide for renderer-specific formatting failures&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-platform-compatibility-guide&quot;&gt;Markdown Across Platforms&lt;/a&gt; — how the same Markdown behaves differently on GitHub, Notion, Obsidian, and Confluence&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Markdown to Slack Formatting: What Actually Works (mrkdwn Explained)</title>
    <link href="https://www.markdowntools.com/blog/markdown-to-slack-formatting-guide" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/markdown-to-slack-formatting-guide</id>
    <published>2026-07-10T00:00:00+00:00</published>
    <updated>2026-07-10T00:00:00+00:00</updated>
    <author><name>Matt Rathbone</name></author>
    <category term="workflows"/>
    <summary>Slack doesn't render standard Markdown — it uses its own mrkdwn format. Here's the full syntax mapping, what breaks (tables, headings, images), and how to convert reliably.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/markdown-to-slack-formatting-guide">&lt;p&gt;If you&amp;#39;ve ever pasted a chunk of Markdown into Slack expecting headings and tables to show up nicely, you&amp;#39;ve already learned the hard way: Slack doesn&amp;#39;t speak Markdown. It has its own lookalike format called &lt;strong&gt;mrkdwn&lt;/strong&gt; (yes, missing the &amp;quot;a&amp;quot; — that&amp;#39;s the actual name Slack uses in its own API docs), and it diverges from standard Markdown in ways that are easy to get wrong.&lt;/p&gt;
&lt;p&gt;This guide covers the full syntax mapping, what Slack simply can&amp;#39;t render, and the gotcha that trips up most people building bots or webhooks: the difference between typing in the Slack message box and sending mrkdwn through the API.&lt;/p&gt;
&lt;h2 id=&quot;markdown-vs-slacks-mrkdwn&quot;&gt;Markdown vs. Slack&amp;#39;s mrkdwn&lt;/h2&gt;
&lt;p&gt;The two syntaxes look similar at a glance, but several common Markdown conventions use different characters in Slack — and a few have no equivalent at all.&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Formatting&lt;/th&gt;
&lt;th&gt;Markdown&lt;/th&gt;
&lt;th&gt;Slack mrkdwn&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Bold&lt;/td&gt;
&lt;td&gt;&lt;code&gt;**bold**&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;*bold*&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Italic&lt;/td&gt;
&lt;td&gt;&lt;code&gt;*italic*&lt;/code&gt; or &lt;code&gt;_italic_&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;_italic_&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strikethrough&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~~strike~~&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~strike~&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inline code&lt;/td&gt;
&lt;td&gt;&lt;code&gt;`code`&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;`code`&lt;/code&gt; (same)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code block&lt;/td&gt;
&lt;td&gt;&lt;code&gt;```code```&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;```code```&lt;/code&gt; (same)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blockquote&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt; quote&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt; quote&lt;/code&gt; (same, one level only)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Link&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[text](url)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;url|text&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heading&lt;/td&gt;
&lt;td&gt;&lt;code&gt;# Heading&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No native heading — use bold instead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unordered list&lt;/td&gt;
&lt;td&gt;&lt;code&gt;- item&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;• item&lt;/code&gt; (see notes below)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ordered list&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1. item&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Not auto-numbered — type numbers manually&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Horizontal rule&lt;/td&gt;
&lt;td&gt;&lt;code&gt;---&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Not supported in message text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Table&lt;/td&gt;
&lt;td&gt;&lt;code&gt;| a | b |&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Not supported at all&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image&lt;/td&gt;
&lt;td&gt;&lt;code&gt;![alt](url)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Not supported inline&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;The single asterisk for bold is the one that catches people most often — it&amp;#39;s the exact opposite of standard Markdown, where a single asterisk means italic. Type &lt;code&gt;*this*&lt;/code&gt; in Slack and you&amp;#39;ll get &lt;strong&gt;bold&lt;/strong&gt;, not italics.&lt;/p&gt;
&lt;h2 id=&quot;the-trap-typing-in-slack-vs-sending-via-api&quot;&gt;The Trap: Typing in Slack vs. Sending via API&lt;/h2&gt;
&lt;p&gt;Here&amp;#39;s the detail that most guides skip, and the one that matters most if you&amp;#39;re building a bot, webhook, or CI notification: &lt;strong&gt;Slack&amp;#39;s message composer and Slack&amp;#39;s API don&amp;#39;t behave identically.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When you type directly into the Slack message box, the client does some helpful auto-formatting as you go — typing &lt;code&gt;- &lt;/code&gt; at the start of a line converts it to a bullet, and typing &lt;code&gt;1. &lt;/code&gt; will auto-number subsequent lines while you&amp;#39;re actively editing. This is a composer-only convenience.&lt;/p&gt;
&lt;p&gt;When you send text programmatically — via &lt;code&gt;chat.postMessage&lt;/code&gt;, an incoming webhook, or a Block Kit &lt;code&gt;mrkdwn&lt;/code&gt; text object — none of that auto-formatting happens. A literal &lt;code&gt;- item&lt;/code&gt; sent through the API renders as the literal characters &lt;code&gt;- item&lt;/code&gt;, not a bullet. If you want real bullets from a bot or script, you need to insert the actual bullet character (&lt;code&gt;•&lt;/code&gt;, &lt;code&gt;Option+8&lt;/code&gt; on Mac, or the Unicode character &lt;code&gt;U+2022&lt;/code&gt;) yourself, or build proper Block Kit list structures. The same applies to numbered lists — the API will not auto-number &lt;code&gt;1.&lt;/code&gt; / &lt;code&gt;2.&lt;/code&gt; / &lt;code&gt;3.&lt;/code&gt; for you.&lt;/p&gt;
&lt;p&gt;If your Markdown-to-Slack workflow runs through automation (a Git-to-Slack notifier, a changelog bot, a CI pipeline), test the actual API output, not just what you see typing manually in a channel.&lt;/p&gt;
&lt;h2 id=&quot;what-doesnt-convert&quot;&gt;What Doesn&amp;#39;t Convert&lt;/h2&gt;
&lt;h3 id=&quot;headings&quot;&gt;Headings&lt;/h3&gt;
&lt;p&gt;Slack&amp;#39;s mrkdwn has no heading syntax at all — no &lt;code&gt;#&lt;/code&gt;, no font-size levels. The only way to get a &amp;quot;heading&amp;quot; look in an ordinary message is to bold the line: &lt;code&gt;*Section Title*&lt;/code&gt;. If you&amp;#39;re using Block Kit (Slack&amp;#39;s structured message format for apps and bots), there&amp;#39;s a dedicated &lt;code&gt;header&lt;/code&gt; block type, but that&amp;#39;s a separate block, not something you write inline in mrkdwn text.&lt;/p&gt;
&lt;h3 id=&quot;tables&quot;&gt;Tables&lt;/h3&gt;
&lt;p&gt;There is no table support in Slack messages — not in mrkdwn, and not in standard Block Kit blocks either. If your Markdown source has tables, you have a few options: convert the table to a formatted list, use Block Kit&amp;#39;s &lt;code&gt;fields&lt;/code&gt; array in a &lt;code&gt;section&lt;/code&gt; block to fake a rough two-column layout, or drop the table and link out to the full document instead. There is no clean equivalent, so if tables matter, don&amp;#39;t rely on message text at all.&lt;/p&gt;
&lt;h3 id=&quot;images&quot;&gt;Images&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;![alt](url)&lt;/code&gt; doesn&amp;#39;t render inline. Slack ignores the syntax entirely and shows it as literal text. To actually display an image in Slack, you need a separate &lt;code&gt;image&lt;/code&gt; block in Block Kit, or you paste the image file itself into the channel. A Markdown image reference converted straight to Slack text will just look broken — most conversion tools (including ours) strip the image markup and keep the alt text so at least the description survives.&lt;/p&gt;
&lt;h3 id=&quot;nested-lists&quot;&gt;Nested Lists&lt;/h3&gt;
&lt;p&gt;Slack doesn&amp;#39;t have a native concept of list nesting depth in mrkdwn. Manually indenting with spaces before a bullet character will usually render with some visual indentation in the client, but it&amp;#39;s not a documented, reliable feature — behavior has changed across Slack client versions. If nesting matters for readability, consider flattening to a single level with prefixes (&lt;code&gt;Frontend: Component X&lt;/code&gt;) rather than depending on indentation.&lt;/p&gt;
&lt;h3 id=&quot;horizontal-rules&quot;&gt;Horizontal Rules&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;---&lt;/code&gt; has no special meaning in Slack message text — it renders as three literal dashes. The closest equivalent in Block Kit is the &lt;code&gt;divider&lt;/code&gt; block, which is a structural element, not something you can type inline.&lt;/p&gt;
&lt;h3 id=&quot;footnotes-definition-lists-task-lists&quot;&gt;Footnotes, Definition Lists, Task Lists&lt;/h3&gt;
&lt;p&gt;None of these have any Slack equivalent. Footnote references (&lt;code&gt;[^1]&lt;/code&gt;) show up as literal bracket text. Task list checkboxes (&lt;code&gt;- [ ]&lt;/code&gt;) render as literal text — Slack has no native inline checkbox in message text (interactive checkboxes exist only as Block Kit UI elements requiring an app, not plain-text formatting).&lt;/p&gt;
&lt;h3 id=&quot;html&quot;&gt;HTML&lt;/h3&gt;
&lt;p&gt;Slack strips or ignores raw HTML in message text entirely. If your Markdown source has embedded HTML for layout tricks, none of it will survive — plan on it disappearing.&lt;/p&gt;
&lt;h2 id=&quot;escaping-characters-for-api-use&quot;&gt;Escaping Characters for API Use&lt;/h2&gt;
&lt;p&gt;If you&amp;#39;re sending mrkdwn through the API rather than typing manually, three characters need HTML-entity escaping because they have special meaning in Slack&amp;#39;s format (particularly &lt;code&gt;&amp;lt;&lt;/code&gt; and &lt;code&gt;&amp;gt;&lt;/code&gt;, which wrap links):&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Character&lt;/th&gt;
&lt;th&gt;Escape as&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;amp;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;lt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Skip this and a message containing a literal &lt;code&gt;&amp;lt;&lt;/code&gt; or &lt;code&gt;&amp;gt;&lt;/code&gt; — say, example code like &lt;code&gt;if (x &amp;lt; y)&lt;/code&gt; — can get mangled or silently dropped, since Slack tries to parse it as the start of a link tag.&lt;/p&gt;
&lt;h2 id=&quot;quick-reference&quot;&gt;Quick Reference&lt;/h2&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Markdown feature&lt;/th&gt;
&lt;th&gt;Slack support&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Bold / italic / strikethrough&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Different characters (see table above)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inline code / code blocks&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Same backtick syntax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blockquote&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Works, single level only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Links&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Different syntax: &lt;code&gt;&amp;lt;url|text&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Headings&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Falls back to bold text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tables&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No equivalent, renders as broken text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Images&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Ignored inline; needs a separate Block Kit block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ordered/unordered lists&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Composer auto-formats; API does not&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nested lists&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Unreliable, client-dependent indentation only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Horizontal rule&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Renders as literal dashes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Footnotes, definition lists, task lists&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No equivalent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Stripped&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;using-the-converter&quot;&gt;Using the Converter&lt;/h2&gt;
&lt;p&gt;Our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-slack&quot;&gt;Markdown to Slack converter&lt;/a&gt; handles the mechanical parts of this conversion automatically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Converts &lt;code&gt;**bold**&lt;/code&gt; to &lt;code&gt;*bold*&lt;/code&gt; and italics to &lt;code&gt;_italic_&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Converts &lt;code&gt;~~strikethrough~~&lt;/code&gt; to &lt;code&gt;~strikethrough~&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Converts headings to bold text (since Slack has no heading syntax)&lt;/li&gt;
&lt;li&gt;Converts &lt;code&gt;[text](url)&lt;/code&gt; links to Slack&amp;#39;s &lt;code&gt;&amp;lt;url|text&amp;gt;&lt;/code&gt; format&lt;/li&gt;
&lt;li&gt;Strips image Markdown while keeping the alt text so the description isn&amp;#39;t lost&lt;/li&gt;
&lt;li&gt;Removes horizontal rules&lt;/li&gt;
&lt;li&gt;Shows a warning badge if it detects a table in your source, since Slack can&amp;#39;t render it&lt;/li&gt;
&lt;li&gt;Protects code blocks and inline code from being touched by the other conversions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Paste your Markdown on the left and get Slack-ready mrkdwn on the right, with a side-by-side comparison so you can see exactly what changed. No signup required.&lt;/p&gt;
&lt;p&gt;This is especially useful for release notes, changelog summaries, or documentation excerpts you&amp;#39;re pasting into a channel — the kind of content that&amp;#39;s authored in Markdown but consumed in Slack.&lt;/p&gt;
&lt;h2 id=&quot;related-posts-and-tools&quot;&gt;Related Posts and Tools&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-slack&quot;&gt;Markdown to Slack converter&lt;/a&gt; — real-time conversion, no signup required&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-platform-compatibility-guide&quot;&gt;Markdown Across Platforms&lt;/a&gt; — how Markdown behaves across GitHub, Notion, Obsidian, and Confluence&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/why-your-markdown-isnt-rendering&quot;&gt;Why Your Markdown Isn&amp;#39;t Rendering&lt;/a&gt; — troubleshooting guide covering renderer-specific quirks like this one&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-to-jira-wiki-markup-guide&quot;&gt;Markdown to Jira Wiki Markup&lt;/a&gt; — the same kind of syntax-mapping problem, different tool&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-for-notion-what-actually-works&quot;&gt;Markdown for Notion&lt;/a&gt; — what converts cleanly (and what doesn&amp;#39;t) when moving Markdown into Notion&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Markdown to Word (DOCX): What Converts Cleanly and What Doesn't</title>
    <link href="https://www.markdowntools.com/blog/markdown-to-word-docx-what-converts-cleanly" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/markdown-to-word-docx-what-converts-cleanly</id>
    <published>2026-07-03T00:00:00+00:00</published>
    <updated>2026-07-03T00:00:00+00:00</updated>
    <author><name>Matt Rathbone</name></author>
    <category term="workflows"/>
    <summary>Converting Markdown to a Word document for stakeholders, legal review, or print? Here's what survives the trip cleanly, what gets mangled, and which conversion method to use.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/markdown-to-word-docx-what-converts-cleanly">&lt;p&gt;Markdown is great until someone who doesn&amp;#39;t use Markdown needs to read your document. A legal team wants track changes. A client wants a Word file they can comment on. A stakeholder wants something they can print and mark up with a pen. None of these people are going to install a Markdown viewer, and pasting raw &lt;code&gt;**bold**&lt;/code&gt; asterisks into an email is not a good look.&lt;/p&gt;
&lt;p&gt;This is the most common reason technical teams end up converting Markdown to Word: not because Word is a better writing tool, but because it&amp;#39;s the format everyone else already has open.&lt;/p&gt;
&lt;p&gt;This guide covers what actually happens when you make that conversion — what comes through clean, what gets lossy or breaks outright, and which conversion method to reach for depending on your situation.&lt;/p&gt;
&lt;h2 id=&quot;why-people-need-markdown-to-word&quot;&gt;Why People Need Markdown to Word&lt;/h2&gt;
&lt;p&gt;A few recurring scenarios:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Non-technical stakeholders&lt;/strong&gt; — product managers, executives, clients — who live in Word and Google Docs, not Git and Markdown editors&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Legal and compliance review&lt;/strong&gt; — redlines, track changes, and signature workflows are built around &lt;code&gt;.docx&lt;/code&gt;, not diffs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Print and physical review&lt;/strong&gt; — some reviewers still want a printed page with a red pen, and Word&amp;#39;s print layout is more predictable than a browser print dialog&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Formal deliverables&lt;/strong&gt; — proposals, contracts, and reports that need to look like a &amp;quot;real document,&amp;quot; complete with headers, page numbers, and a title page&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Handoff to teams outside engineering&lt;/strong&gt; — marketing, sales, and HR often maintain their own copies of documentation in Word because that&amp;#39;s the tool their org standardizes on&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you&amp;#39;re writing in Markdown because it&amp;#39;s fast, version-controllable, and diff-friendly, but the last step of the workflow needs to land in someone else&amp;#39;s inbox as a &lt;code&gt;.docx&lt;/code&gt;, you need a reliable conversion path — and &amp;quot;reliable&amp;quot; is doing a lot of work in that sentence, because Word&amp;#39;s formatting model doesn&amp;#39;t map onto Markdown&amp;#39;s the way you&amp;#39;d hope.&lt;/p&gt;
&lt;h2 id=&quot;what-converts-cleanly&quot;&gt;What Converts Cleanly&lt;/h2&gt;
&lt;p&gt;The core of Markdown — the stuff that maps directly onto Word&amp;#39;s built-in styles — converts well across every method covered below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Headings&lt;/strong&gt; (&lt;code&gt;#&lt;/code&gt; through &lt;code&gt;######&lt;/code&gt;) → Word&amp;#39;s Heading 1–6 styles, which means your table of contents will auto-generate correctly if the target app supports it&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bold and italic&lt;/strong&gt; (&lt;code&gt;**bold**&lt;/code&gt;, &lt;code&gt;*italic*&lt;/code&gt;) → Word&amp;#39;s native bold/italic character formatting&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unordered and ordered lists&lt;/strong&gt; → Word list styles, including basic nested lists (one or two levels deep)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tables&lt;/strong&gt; (with a proper separator row) → real Word tables, not just formatted text — you can resize columns and apply table styles afterward&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Links&lt;/strong&gt; (&lt;code&gt;[text](url)&lt;/code&gt;) → clickable hyperlinks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Images&lt;/strong&gt; (&lt;code&gt;![alt](path)&lt;/code&gt;) → embedded images, provided the path resolves (see below)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blockquotes&lt;/strong&gt; → Word&amp;#39;s Quote or Intense Quote paragraph style, depending on the converter&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Horizontal rules&lt;/strong&gt; (&lt;code&gt;---&lt;/code&gt;) → a page-break-like divider line&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a straightforward prose document — headings, paragraphs, a few lists, maybe a table — any of the methods below will get you a usable Word file with minimal cleanup.&lt;/p&gt;
&lt;h2 id=&quot;whats-lossy-or-breaks&quot;&gt;What&amp;#39;s Lossy or Breaks&lt;/h2&gt;
&lt;p&gt;This is where it gets interesting, because &amp;quot;Markdown to Word&amp;quot; isn&amp;#39;t a lossless round trip in either direction. Several Markdown features have no real equivalent in Word&amp;#39;s document model.&lt;/p&gt;
&lt;h3 id=&quot;syntax-highlighted-code-blocks&quot;&gt;Syntax-Highlighted Code Blocks&lt;/h3&gt;
&lt;p&gt;Word has no concept of a &amp;quot;code block with syntax highlighting.&amp;quot; A fenced code block like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&lt;span class=&quot;hljs-code&quot;&gt;```python
def hello():
    print(&amp;quot;Hello, world!&amp;quot;)
```&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;becomes, at best, monospaced text in a gray-shaded box with no color coding — Pandoc does this reasonably well with its &lt;code&gt;--highlight-style&lt;/code&gt; option. At worst, with a naive converter, you get default-font text with the language tag (&lt;code&gt;python&lt;/code&gt;) left dangling above it as plain text. If your document is full of code samples, expect to spend time manually reapplying a &amp;quot;Code&amp;quot; character style after conversion, or accept that the code will just look like indented text.&lt;/p&gt;
&lt;h3 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h3&gt;
&lt;p&gt;Markdown footnotes (&lt;code&gt;[^1]&lt;/code&gt; with a corresponding &lt;code&gt;[^1]: definition&lt;/code&gt;) map onto Word&amp;#39;s native footnote/endnote system reasonably well &lt;em&gt;if&lt;/em&gt; your converter supports it — Pandoc does. Weaker converters either drop the footnote marker entirely or dump the footnote text inline at the point of reference, which reads strangely and loses the actual footnote formatting (small superscript number, bottom-of-page text).&lt;/p&gt;
&lt;h3 id=&quot;nested-lists&quot;&gt;Nested Lists&lt;/h3&gt;
&lt;p&gt;Markdown handles nested lists with indentation, and in theory this maps to Word&amp;#39;s multilevel list styles. In practice, list nesting is one of the most fragile parts of any Markdown-to-anything conversion. Watch for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Inconsistent indentation width getting collapsed to the wrong nesting level&lt;/li&gt;
&lt;li&gt;Mixed ordered/unordered nested lists (a numbered list inside a bulleted one) losing their list type on conversion&lt;/li&gt;
&lt;li&gt;List numbering restarting unexpectedly when a code block or paragraph interrupts a list&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your source Markdown has three or more levels of nesting, check the converted document by hand — this is the single most common thing that needs manual fixing after conversion.&lt;/p&gt;
&lt;h3 id=&quot;yaml-frontmatter&quot;&gt;YAML Frontmatter&lt;/h3&gt;
&lt;p&gt;If your Markdown source starts with frontmatter like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;---
title: Q3 Planning Doc
author: Jane Smith
&lt;span class=&quot;hljs-section&quot;&gt;date: 2026-07-01
---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Word has no frontmatter concept. Depending on the converter, this either gets dropped silently (best case), or it renders as a literal block of &lt;code&gt;title: Q3 Planning Doc&lt;/code&gt; text at the top of the document (worse case). Strip frontmatter before converting, or use a tool that recognizes and removes it automatically.&lt;/p&gt;
&lt;h3 id=&quot;mermaid-diagrams&quot;&gt;Mermaid Diagrams&lt;/h3&gt;
&lt;p&gt;A fenced &lt;code&gt;```mermaid&lt;/code&gt; block is diagram &lt;em&gt;source code&lt;/em&gt; — a description of a flowchart or sequence diagram, not the diagram itself. Word cannot render Mermaid. On conversion, you&amp;#39;ll get the raw Mermaid syntax printed as plain text, which is meaningless to a Word reader. If your document depends on diagrams, render them to PNG or SVG images first and embed the images, or accept that the Word version needs manual diagram insertion.&lt;/p&gt;
&lt;h3 id=&quot;github-style-alerts&quot;&gt;GitHub-Style Alerts&lt;/h3&gt;
&lt;p&gt;GitHub Flavored Markdown alerts like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; [!WARNING]&lt;/span&gt;
&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; This endpoint is deprecated and will be removed in v3.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;render as colored, icon-labeled callout boxes on GitHub, but there&amp;#39;s no equivalent block-quote-with-icon primitive in standard Markdown-to-Word conversion. You&amp;#39;ll typically get a plain block quote with the literal text &lt;code&gt;[!WARNING]&lt;/code&gt; still sitting in it. If the alert type matters for your reader, convert it to plain prose (&amp;quot;&lt;strong&gt;Warning:&lt;/strong&gt; This endpoint is deprecated...&amp;quot;) before converting to Word, since Word&amp;#39;s Quote style alone doesn&amp;#39;t communicate severity the way a colored GitHub alert does.&lt;/p&gt;
&lt;h3 id=&quot;task-list-checkboxes&quot;&gt;Task List Checkboxes&lt;/h3&gt;
&lt;p&gt;Markdown checkboxes (&lt;code&gt;- [ ]&lt;/code&gt; and &lt;code&gt;- [x]&lt;/code&gt;) are interactive in GitHub, GitLab, and Notion. In Word, they typically convert to plain bullet points with a literal &lt;code&gt;☐&lt;/code&gt; or &lt;code&gt;☒&lt;/code&gt; character, or sometimes just the raw &lt;code&gt;[ ]&lt;/code&gt; text — not an actual clickable/checkable Word content control. If you need genuinely interactive checkboxes in the Word file, you&amp;#39;ll need to insert Word&amp;#39;s native checkbox content controls manually after conversion; nothing on the Markdown side of the pipe maps to them automatically.&lt;/p&gt;
&lt;h2 id=&quot;quick-reference&quot;&gt;Quick Reference&lt;/h2&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Markdown feature&lt;/th&gt;
&lt;th&gt;Converts cleanly?&lt;/th&gt;
&lt;th&gt;What happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Headings&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Maps to Word Heading styles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bold, italic&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Native character formatting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lists (1–2 levels)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Native list styles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tables&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Real, editable Word tables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Links&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Clickable hyperlinks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Images (resolvable paths)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Embedded images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blockquotes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Quote paragraph style&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code blocks&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Monospace text; syntax highlighting depends on converter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Footnotes&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Works with Pandoc; weaker tools inline or drop them&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deeply nested lists (3+ levels)&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Often collapses or misnumbers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;YAML frontmatter&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Dropped or rendered as literal text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mermaid diagrams&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Renders as raw diagram source text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub-style alerts&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Becomes a plain block quote&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Task checkboxes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Becomes plain bullets, not interactive controls&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;comparing-conversion-approaches&quot;&gt;Comparing Conversion Approaches&lt;/h2&gt;
&lt;p&gt;There isn&amp;#39;t one &amp;quot;correct&amp;quot; way to do this conversion — the right tool depends on how much control you need and how technical your workflow is.&lt;/p&gt;
&lt;h3 id=&quot;pandoc-command-line&quot;&gt;Pandoc (Command Line)&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://pandoc.org/&quot;&gt;Pandoc&lt;/a&gt; is the standard for this conversion and the engine behind most &amp;quot;Markdown to Word&amp;quot; tools you&amp;#39;ll find, including several of the online converters below. Running:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;pandoc input.md -o output.docx
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;gets you a solid baseline conversion with good table, footnote, and list support. The real power is in customization — you can supply a reference &lt;code&gt;.docx&lt;/code&gt; file with &lt;code&gt;--reference-doc=template.docx&lt;/code&gt; so the output inherits your organization&amp;#39;s fonts, heading styles, and margins instead of Pandoc&amp;#39;s plain defaults. This is the best option if you&amp;#39;re generating Word documents as part of an automated pipeline (say, a GitHub Action that builds a &lt;code&gt;.docx&lt;/code&gt; release artifact from your docs repo) or if you need precise control over the output styling.&lt;/p&gt;
&lt;p&gt;The tradeoff: it&amp;#39;s a command-line tool. Someone on your team needs to install it, and it&amp;#39;s not something you hand to a non-technical stakeholder who just wants to paste some Markdown and get a file.&lt;/p&gt;
&lt;h3 id=&quot;words-own-paste-from-markdown&quot;&gt;Word&amp;#39;s Own Paste-from-Markdown&lt;/h3&gt;
&lt;p&gt;Recent versions of Microsoft Word (365) can autodetect Markdown formatting when you paste plain text — typing or pasting &lt;code&gt;**bold**&lt;/code&gt; and having Word convert it to actual bold as you type, similar to how it autoformats a line starting with &lt;code&gt;1.&lt;/code&gt; into a numbered list. This only works for the most basic syntax (bold, italic, headings via &lt;code&gt;#&lt;/code&gt;, simple lists) and is inconsistent between Word versions and platforms (desktop vs. web vs. Mac). It doesn&amp;#39;t handle tables, code blocks, or anything more complex than inline formatting. Useful for a quick one-paragraph paste; not a real conversion pipeline.&lt;/p&gt;
&lt;h3 id=&quot;online-converters&quot;&gt;Online Converters&lt;/h3&gt;
&lt;p&gt;Web-based converters (many of them Pandoc running server-side behind a simple upload form) are the path of least resistance for a one-off conversion — no installation, paste or upload, download a &lt;code&gt;.docx&lt;/code&gt;. The tradeoffs are the usual ones for any &amp;quot;paste your content into a random website&amp;quot; tool: check what happens to your data (does it get stored, logged, or used for anything?), and be aware that free tiers often add limits on file size or strip formatting to encourage a paid upgrade.&lt;/p&gt;
&lt;h3 id=&quot;our-markdown-to-word-converter&quot;&gt;Our Markdown to Word Converter&lt;/h3&gt;
&lt;p&gt;We&amp;#39;re building a &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-docx&quot;&gt;Markdown to Word (DOCX) converter&lt;/a&gt; that handles the common case — paste your Markdown, get a clean &lt;code&gt;.docx&lt;/code&gt; — without a command-line tool or an account. Like our other conversion tools (&lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-confluence&quot;&gt;Markdown to Confluence&lt;/a&gt;, &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-notion&quot;&gt;Markdown to Notion&lt;/a&gt;), the goal is to handle the lossy edge cases explicitly rather than silently mangling them: frontmatter gets stripped with a warning instead of dumped as literal text, GitHub-style alerts get flattened into labeled block quotes instead of leaving &lt;code&gt;[!WARNING]&lt;/code&gt; sitting in the output, and Mermaid blocks get flagged so you know to replace them with an image rather than discovering a wall of diagram syntax after you&amp;#39;ve already emailed the file to a client.&lt;/p&gt;
&lt;p&gt;If you need reference-template styling or a scripted pipeline, Pandoc is still the right tool. If you want a fast, no-install way to hand someone a Word doc, that&amp;#39;s what we built this for.&lt;/p&gt;
&lt;h2 id=&quot;a-practical-workflow&quot;&gt;A Practical Workflow&lt;/h2&gt;
&lt;p&gt;For anything beyond a quick one-off paste, this order of operations avoids most of the pain described above:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Strip or note frontmatter&lt;/strong&gt; before converting — decide if any of it (title, author, date) should become part of the Word document&amp;#39;s actual title page instead of just disappearing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Render diagrams to images&lt;/strong&gt; first. Don&amp;#39;t let Mermaid or other diagram syntax hit the converter as text.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flatten GitHub-style alerts&lt;/strong&gt; into plain, clearly-labeled text if the distinction between &amp;quot;note&amp;quot; and &amp;quot;warning&amp;quot; matters to your reader.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check nested lists by hand&lt;/strong&gt; after conversion — this is the highest-probability place for something to look wrong.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reapply a template or style set&lt;/strong&gt; in Word if you need consistent branding (fonts, headers, margins) — most converters, ours included, produce well-structured but plainly-styled documents, and matching your organization&amp;#39;s house style is a separate step.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;related-reading&quot;&gt;Related Reading&lt;/h2&gt;
&lt;p&gt;If you&amp;#39;re dealing with Markdown across multiple destinations, not just Word, these guides cover related ground:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-platform-compatibility-guide&quot;&gt;Markdown Across Platforms: What Actually Works in GitHub, Notion, Obsidian, and Confluence&lt;/a&gt; — the same &amp;quot;what converts cleanly&amp;quot; treatment for other platforms&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/why-your-markdown-isnt-rendering&quot;&gt;Why Your Markdown Isn&amp;#39;t Rendering: A Troubleshooting Guide&lt;/a&gt; — if your Markdown is breaking somewhere other than a Word export&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-for-technical-writers-docs-as-code&quot;&gt;Markdown for Technical Writers: The Docs-as-Code Workflow Guide&lt;/a&gt; — for teams trying to keep Markdown as the source of truth while still producing Word deliverables downstream&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/how-to-convert-word-documents-to-markdown&quot;&gt;How to Convert Word Documents to Markdown&lt;/a&gt; — the reverse direction, if you&amp;#39;re pulling content out of Word and into a Markdown-based workflow&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And when you&amp;#39;re ready to convert, try the &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-docx&quot;&gt;Markdown to Word (DOCX) converter&lt;/a&gt; — paste your Markdown, get a clean Word document, no installation required.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Markdown for Notion: What Actually Works and What Doesn't</title>
    <link href="https://www.markdowntools.com/blog/markdown-for-notion-what-actually-works" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/markdown-for-notion-what-actually-works</id>
    <published>2026-06-26T00:00:00+00:00</published>
    <updated>2026-06-26T00:00:00+00:00</updated>
    <author><name>Matt Rathbone</name></author>
    <category term="workflows"/>
    <summary>A practical guide to importing, writing, and exporting Markdown in Notion — what converts cleanly, what breaks, and reliable workarounds for each case.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/markdown-for-notion-what-actually-works">&lt;p&gt;Notion markets itself as Markdown-friendly, and for basic formatting it mostly is. But spend enough time moving documents between Notion and the rest of your toolchain and you&amp;#39;ll hit the edge cases: frontmatter that renders as prose, tables that become databases, Mermaid diagrams that become plain text, and HTML that disappears entirely.&lt;/p&gt;
&lt;p&gt;This is a guide to what actually happens — not what the docs say should happen.&lt;/p&gt;
&lt;h2 id=&quot;how-notion-handles-markdown-input&quot;&gt;How Notion Handles Markdown Input&lt;/h2&gt;
&lt;p&gt;Notion has three different ways to get Markdown in, and they don&amp;#39;t all behave the same:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Reliability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Paste&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Short snippets, quick formatting&lt;/td&gt;
&lt;td&gt;Medium — tables and code blocks often break&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;File import&lt;/strong&gt; (drag .md file)&lt;/td&gt;
&lt;td&gt;Full documents, preserved structure&lt;/td&gt;
&lt;td&gt;High — most reliable option&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;/markdown&lt;/code&gt; slash command&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pasting a block of Markdown text&lt;/td&gt;
&lt;td&gt;Medium — same caveats as paste&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;File import is more reliable than paste.&lt;/strong&gt; When you paste Markdown, Notion processes it in real time, which can produce odd results with complex tables and fenced code blocks. Dragging a &lt;code&gt;.md&lt;/code&gt; file into the sidebar triggers Notion&amp;#39;s dedicated importer, which handles the format more consistently.&lt;/p&gt;
&lt;h2 id=&quot;what-converts-cleanly&quot;&gt;What Converts Cleanly&lt;/h2&gt;
&lt;p&gt;These elements survive the trip from Markdown to Notion reliably:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Headings&lt;/strong&gt; (&lt;code&gt;#&lt;/code&gt;, &lt;code&gt;##&lt;/code&gt;, &lt;code&gt;###&lt;/code&gt;) → Notion heading blocks (H1, H2, H3)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bold and italic&lt;/strong&gt; (&lt;code&gt;**bold**&lt;/code&gt;, &lt;code&gt;*italic*&lt;/code&gt;) → rich text formatting&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inline code&lt;/strong&gt; (&lt;code&gt;`code`&lt;/code&gt;) → inline code formatting&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fenced code blocks&lt;/strong&gt; with language labels → Notion code blocks with syntax highlighting&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unordered lists&lt;/strong&gt; (&lt;code&gt;-&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;) → bulleted list blocks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ordered lists&lt;/strong&gt; (&lt;code&gt;1.&lt;/code&gt;, &lt;code&gt;2.&lt;/code&gt;) → numbered list blocks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Checkboxes&lt;/strong&gt; (&lt;code&gt;- [ ]&lt;/code&gt;, &lt;code&gt;- [x]&lt;/code&gt;) → to-do blocks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Links&lt;/strong&gt; (&lt;code&gt;[text](url)&lt;/code&gt;) → hyperlinks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blockquotes&lt;/strong&gt; (&lt;code&gt;&amp;gt;&lt;/code&gt;) → Notion quote blocks&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Horizontal rules&lt;/strong&gt; (&lt;code&gt;---&lt;/code&gt;) → divider blocks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For plain prose documents, Notion is actually solid. The problems start with the more complex elements.&lt;/p&gt;
&lt;h2 id=&quot;what-breaks-or-gets-transformed&quot;&gt;What Breaks or Gets Transformed&lt;/h2&gt;
&lt;h3 id=&quot;yaml-frontmatter&quot;&gt;YAML Frontmatter&lt;/h3&gt;
&lt;p&gt;Notion has no concept of frontmatter. If your document starts with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;---
title: My Document
&lt;span class=&quot;hljs-section&quot;&gt;date: 2026-06-26
---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notion imports this as plain text — the dashes become a divider block, and &lt;code&gt;title: My Document&lt;/code&gt; renders as a heading or prose depending on context. If you&amp;#39;re importing docs that have frontmatter, strip it before importing, or use our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-notion&quot;&gt;Markdown to Notion converter&lt;/a&gt;, which removes frontmatter automatically and warns you what was stripped.&lt;/p&gt;
&lt;h3 id=&quot;tables&quot;&gt;Tables&lt;/h3&gt;
&lt;p&gt;Tables are the trickiest part. Notion renders them differently depending on context:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In the &lt;strong&gt;body of a page&lt;/strong&gt;, a Markdown table becomes a simple inline table (not a database)&lt;/li&gt;
&lt;li&gt;When &lt;strong&gt;imported via file&lt;/strong&gt;, tables often become Notion databases with property columns&lt;/li&gt;
&lt;li&gt;If the &lt;strong&gt;separator row is missing&lt;/strong&gt; (&lt;code&gt;|---|---|&lt;/code&gt;), the table may just render as pipe-separated text&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Notion is strict about the GFM table format. Missing or malformed separator rows reliably break table rendering.&lt;/p&gt;
&lt;p&gt;What Notion tables &lt;strong&gt;don&amp;#39;t&lt;/strong&gt; support:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Column alignment (&lt;code&gt;:---:&lt;/code&gt; / &lt;code&gt;---:&lt;/code&gt; is ignored)&lt;/li&gt;
&lt;li&gt;Multi-line cells&lt;/li&gt;
&lt;li&gt;Colspan or rowspan&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;images&quot;&gt;Images&lt;/h3&gt;
&lt;p&gt;Image syntax (&lt;code&gt;![alt](url)&lt;/code&gt;) works for external URLs. If your images use relative paths (e.g., &lt;code&gt;![chart](./images/chart.png)&lt;/code&gt;), they&amp;#39;ll break — Notion can&amp;#39;t resolve relative filesystem paths. Convert relative paths to absolute URLs before importing.&lt;/p&gt;
&lt;h3 id=&quot;html&quot;&gt;HTML&lt;/h3&gt;
&lt;p&gt;Notion strips HTML entirely. Any &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;summary&amp;gt;&lt;/code&gt;, or other HTML elements in your Markdown will disappear on import. If you&amp;#39;re using HTML for collapsible sections, badges, or custom alignment, those won&amp;#39;t survive.&lt;/p&gt;
&lt;h3 id=&quot;mermaid-diagrams&quot;&gt;Mermaid Diagrams&lt;/h3&gt;
&lt;p&gt;Notion does not render Mermaid diagrams. A fenced code block tagged with &lt;code&gt;mermaid&lt;/code&gt; will import as a plain code block containing the diagram source — not a rendered diagram. This trips people up because GitHub, GitLab, and several documentation tools do render Mermaid natively.&lt;/p&gt;
&lt;p&gt;If you have Mermaid diagrams, your options are: export them as images and embed, or use Notion&amp;#39;s native embed integration (which requires a third-party service).&lt;/p&gt;
&lt;h3 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h3&gt;
&lt;p&gt;Standard Markdown footnote syntax (&lt;code&gt;[^1]&lt;/code&gt;) isn&amp;#39;t supported by Notion. On import, footnote references typically disappear and the footnote definitions render as plain text at the bottom of the page. Our converter inlines footnotes — replacing &lt;code&gt;[^1]&lt;/code&gt; references with the footnote text in italics — which is the most readable fallback.&lt;/p&gt;
&lt;h3 id=&quot;github-style-alerts&quot;&gt;GitHub-Style Alerts&lt;/h3&gt;
&lt;p&gt;GitHub Flavored Markdown alerts like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; [!NOTE]&lt;/span&gt;
&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; This is an important note.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;...render in GitHub as styled callout boxes, but Notion imports them as plain blockquotes. The &lt;code&gt;[!NOTE]&lt;/code&gt; text appears literally in the quote. In newer Notion workspaces you can manually convert the blockquote to a callout block after import, but it&amp;#39;s a manual step.&lt;/p&gt;
&lt;h2 id=&quot;exporting-markdown-from-notion&quot;&gt;Exporting Markdown from Notion&lt;/h2&gt;
&lt;p&gt;Notion&amp;#39;s export (Page → Export → Markdown &amp;amp; CSV) produces reasonable Markdown, but with quirks to know about:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tables export as CSV files&lt;/strong&gt; — not inline Markdown tables. If your page contains a database, the export creates a separate &lt;code&gt;.csv&lt;/code&gt; file and links to it from the Markdown. This roundtrip doesn&amp;#39;t work well for docs-as-code workflows where you expect embedded tables.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Internal page links&lt;/strong&gt; export as full Notion URLs — not relative paths. An internal link to another Notion page becomes something like &lt;code&gt;[Page Title](https://www.notion.so/workspace/abc123...)&lt;/code&gt;. These links break when you move content out of Notion.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Callout blocks&lt;/strong&gt; export as blockquotes with the emoji prepended as plain text.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nested pages&lt;/strong&gt; export as subdirectories. The folder structure maps to page hierarchy, which is useful if you want to mirror the Notion structure but inconvenient if you want a flat directory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Whitespace&lt;/strong&gt; in exported tables includes trailing spaces for column alignment. Most parsers handle this fine, but some produce artifacts.&lt;/p&gt;
&lt;h2 id=&quot;notion-api-and-markdown&quot;&gt;Notion API and Markdown&lt;/h2&gt;
&lt;p&gt;If you&amp;#39;re using the Notion API to create or update pages programmatically, you can&amp;#39;t POST Markdown directly. The API uses Notion&amp;#39;s own block format — each heading, paragraph, list item, and code block is a separate JSON object. You need to convert Markdown to block format before posting.&lt;/p&gt;
&lt;p&gt;This matters for teams automating documentation workflows: syncing a Git repo to Notion, generating pages from templates, or importing content on a schedule. The Notion API is capable, but it expects its own block format, not Markdown strings.&lt;/p&gt;
&lt;h2 id=&quot;quick-reference-import-compatibility&quot;&gt;Quick Reference: Import Compatibility&lt;/h2&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Markdown feature&lt;/th&gt;
&lt;th&gt;Imports correctly?&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Headings H1–H3&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bold, italic&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inline code&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fenced code blocks&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Language labels preserved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unordered / ordered lists&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Checkboxes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Links&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blockquotes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tables (with separator row)&lt;/td&gt;
&lt;td&gt;Mostly&lt;/td&gt;
&lt;td&gt;No alignment, colspan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;YAML frontmatter&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Renders as prose / divider&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Images (relative paths)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Absolute URLs only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Stripped entirely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mermaid diagrams&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Renders as plain code block&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Footnotes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Definitions appear as text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub-style alerts&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Renders as plain blockquote&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;using-the-converter-tool&quot;&gt;Using the Converter Tool&lt;/h2&gt;
&lt;p&gt;If you&amp;#39;re regularly moving content into Notion, our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-notion&quot;&gt;Markdown to Notion converter&lt;/a&gt; handles the pre-processing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Strips YAML frontmatter (with a warning listing what was removed)&lt;/li&gt;
&lt;li&gt;Inlines footnotes as italic text inline with the reference&lt;/li&gt;
&lt;li&gt;Converts GitHub-style alerts to labeled blockquotes&lt;/li&gt;
&lt;li&gt;Removes HTML tags (with a warning)&lt;/li&gt;
&lt;li&gt;Flags Mermaid diagrams so you can handle them manually&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Paste your Markdown on the left, get Notion-ready Markdown on the right. No signup required.&lt;/p&gt;
&lt;h2 id=&quot;related-posts-and-tools&quot;&gt;Related Posts and Tools&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-notion&quot;&gt;Markdown to Notion converter&lt;/a&gt; — cleans up Markdown before importing&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-platform-compatibility-guide&quot;&gt;Markdown Across Platforms&lt;/a&gt; — how Markdown behaves in GitHub, Obsidian, Confluence, and more&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/obsidian-vs-notion-vs-roam-note-taking-comparison&quot;&gt;Obsidian vs Notion vs Roam Research&lt;/a&gt; — which note-taking tool fits your workflow&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-in-confluence-what-works&quot;&gt;Markdown in Confluence&lt;/a&gt; — the same treatment for Confluence users&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/html-to-markdown&quot;&gt;HTML to Markdown&lt;/a&gt; — extract Markdown from Notion&amp;#39;s HTML export&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Obsidian Plugins for Developers: The Essential Setup (2026)</title>
    <link href="https://www.markdowntools.com/blog/obsidian-plugins-for-developers" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/obsidian-plugins-for-developers</id>
    <published>2026-06-26T00:00:00+00:00</published>
    <updated>2026-06-26T00:00:00+00:00</updated>
    <author><name>Matthew</name></author>
    <category term="tools"/>
    <summary>The best Obsidian plugins for software developers — from code-aware note-taking and project tracking to Git sync and API access. Opinionated recommendations with real configuration examples.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/obsidian-plugins-for-developers">&lt;p&gt;Obsidian&amp;#39;s plugin ecosystem has over 1,500 community plugins. Most of them are noise. After spending two years using Obsidian as a developer&amp;#39;s second brain — for architecture notes, runbooks, incident retrospectives, and project planning — these are the ones I&amp;#39;d install on a fresh vault today.&lt;/p&gt;
&lt;p&gt;This guide covers plugins that are genuinely useful for software developers specifically, not general note-taking plugins that any knowledge worker might use.&lt;/p&gt;
&lt;h2 id=&quot;the-short-list&quot;&gt;The short list&lt;/h2&gt;
&lt;p&gt;If you&amp;#39;re in a hurry, install these seven and you&amp;#39;ll be well set up:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plugin&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dataview&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Query your notes like a database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Templater&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Powerful templates with JavaScript&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Obsidian Git&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Auto-sync vault via Git&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code Block Enhancer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Copy button, language badges on code blocks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Advanced Tables&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Spreadsheet-style table editing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tasks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Track to-dos across your entire vault&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Linter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Keep Markdown consistent and clean&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Read on for installation tips, configuration, and the use cases each one handles best.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1-dataview--query-your-vault-like-a-database&quot;&gt;1. Dataview — query your vault like a database&lt;/h2&gt;
&lt;p&gt;Dataview is the most powerful plugin in the Obsidian ecosystem. It lets you write SQL-like queries over your notes, pulling in data from YAML frontmatter and inline fields.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Install:&lt;/strong&gt; Community plugins → search &amp;quot;Dataview&amp;quot;&lt;/p&gt;
&lt;h3 id=&quot;why-developers-love-it&quot;&gt;Why developers love it&lt;/h3&gt;
&lt;p&gt;You can build dashboards that automatically populate from your notes. A few real examples:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;All open tasks tagged &lt;code&gt;#backend&lt;/code&gt; created this week:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;```dataview
TASK FROM #backend
WHERE !completed AND file.ctime &amp;gt;= date(today) - dur(7 days)
```
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;List of all architecture decisions (ADRs) in the &lt;code&gt;decisions/&lt;/code&gt; folder:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;```dataview
TABLE file.ctime AS &amp;quot;Created&amp;quot;, status, context
FROM &amp;quot;decisions&amp;quot;
SORT file.ctime DESC
```
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;A dashboard of all projects with their status from frontmatter:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;```dataview
TABLE status, stack, last-updated
FROM &amp;quot;projects&amp;quot;
WHERE status != &amp;quot;done&amp;quot;
SORT last-updated DESC
```
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;configuration-tips&quot;&gt;Configuration tips&lt;/h3&gt;
&lt;p&gt;Enable JavaScript queries in Dataview settings — this unlocks &lt;code&gt;dataviewjs&lt;/code&gt; blocks where you can write arbitrary JS against your vault data. It&amp;#39;s what separates useful queries from genuinely powerful ones.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2-templater--templates-that-actually-do-things&quot;&gt;2. Templater — templates that actually do things&lt;/h2&gt;
&lt;p&gt;The core Templates plugin is fine for static snippets. Templater is what you reach for when you want templates that &lt;em&gt;run code&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Install:&lt;/strong&gt; Community plugins → search &amp;quot;Templater&amp;quot;&lt;/p&gt;
&lt;h3 id=&quot;developer-specific-use-cases&quot;&gt;Developer-specific use cases&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;New ADR template&lt;/strong&gt; — auto-populates the date and a sequential number:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
title: &amp;lt;% tp.file.title %&amp;gt;
date: &amp;lt;% tp.date.now(&amp;quot;YYYY-MM-DD&amp;quot;) %&amp;gt;
status: proposed
---

## Context

## Decision

## Consequences
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;New incident retrospective&lt;/strong&gt; — prompts for severity and affected service:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
date: &amp;lt;% tp.date.now(&amp;quot;YYYY-MM-DD&amp;quot;) %&amp;gt;
severity: &amp;lt;%* tR += await tp.system.prompt(&amp;quot;Severity (P0/P1/P2)?&amp;quot;) %&amp;gt;
service: &amp;lt;%* tR += await tp.system.prompt(&amp;quot;Affected service?&amp;quot;) %&amp;gt;
---

## Timeline

## Root cause

## Action items
- [ ] 
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;New daily note&lt;/strong&gt; — pulls in incomplete tasks from yesterday:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# &amp;lt;% tp.date.now(&amp;quot;dddd, MMMM D&amp;quot;) %&amp;gt;

## Carry-forward
&amp;lt;%* 
const yesterday = tp.date.now(&amp;quot;YYYY-MM-DD&amp;quot;, -1)
const yNote = app.vault.getAbstractFileByPath(`daily/${yesterday}.md`)
// add your task extraction logic here
%&amp;gt;

## Today&amp;#39;s focus

## Notes
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;configuration-tips-1&quot;&gt;Configuration tips&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Set the &lt;strong&gt;template folder&lt;/strong&gt; in Templater settings (I use &lt;code&gt;_templates/&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Enable &lt;strong&gt;Trigger Templater on new file creation&lt;/strong&gt; so templates auto-run when you create a note from a template&lt;/li&gt;
&lt;li&gt;Bind a hotkey to &lt;em&gt;Templater: Open Insert Template modal&lt;/em&gt; — I use &lt;code&gt;Ctrl+T&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3-obsidian-git--vault-version-control&quot;&gt;3. Obsidian Git — vault version control&lt;/h2&gt;
&lt;p&gt;Obsidian Git syncs your vault to a Git repository automatically. For developers, this is the obvious choice over Obsidian Sync: you get full version history, branching, and you own your data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Install:&lt;/strong&gt; Community plugins → search &amp;quot;Obsidian Git&amp;quot;&lt;/p&gt;
&lt;h3 id=&quot;setup-for-developers&quot;&gt;Setup for developers&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Init a Git repo in your vault folder: &lt;code&gt;git init &amp;amp;&amp;amp; git remote add origin &amp;lt;your-repo-url&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Install the plugin and set &lt;strong&gt;Auto-pull interval&lt;/strong&gt; (I use 10 minutes) and &lt;strong&gt;Auto-push after auto-commit&lt;/strong&gt; to on&lt;/li&gt;
&lt;li&gt;Set a &lt;strong&gt;Commit message template&lt;/strong&gt; — I use &lt;code&gt;vault: {{date}} auto-commit&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;what-it-gives-you&quot;&gt;What it gives you&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Full &lt;code&gt;git log&lt;/code&gt; on your notes — see exactly when you changed an idea&lt;/li&gt;
&lt;li&gt;Branch your vault for major projects (e.g., &lt;code&gt;feature/q3-planning&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Team shared vaults via a private repo — though Obsidian Sync is smoother for real-time collaboration&lt;/li&gt;
&lt;li&gt;Backup without a subscription fee&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Caveat:&lt;/strong&gt; Sync conflicts when editing from multiple devices simultaneously are painful. If you need seamless multi-device sync, combine Obsidian Sync with Git for backup only, or use iCloud/Dropbox and accept the occasional conflict.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;4-code-block-enhancer--make-code-blocks-developer-friendly&quot;&gt;4. Code Block Enhancer — make code blocks developer-friendly&lt;/h2&gt;
&lt;p&gt;The default Obsidian code block experience is minimal: no copy button, no language badge, no line numbers. Code Block Enhancer adds all of this with zero configuration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Install:&lt;/strong&gt; Community plugins → search &amp;quot;Code Block Enhancer&amp;quot;&lt;/p&gt;
&lt;p&gt;After installing, every code block gets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;copy button&lt;/strong&gt; in the top-right corner&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;language badge&lt;/strong&gt; (shows the syntax language)&lt;/li&gt;
&lt;li&gt;Optional &lt;strong&gt;line numbers&lt;/strong&gt; (enable in settings)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&amp;#39;s a tiny plugin that makes a disproportionate difference when your vault is full of shell commands, SQL snippets, and configuration examples.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;5-advanced-tables--actual-table-editing&quot;&gt;5. Advanced Tables — actual table editing&lt;/h2&gt;
&lt;p&gt;Markdown tables are famously painful to type. Advanced Tables turns them into a spreadsheet-style experience.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Install:&lt;/strong&gt; Community plugins → search &amp;quot;Advanced Tables&amp;quot;&lt;/p&gt;
&lt;h3 id=&quot;what-it-does&quot;&gt;What it does&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Press &lt;code&gt;Tab&lt;/code&gt; to move between cells (auto-aligns the table)&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;Enter&lt;/code&gt; to add a new row&lt;/li&gt;
&lt;li&gt;Sort columns, add/delete rows and columns via a right-click menu&lt;/li&gt;
&lt;li&gt;Auto-formats the pipe characters so the raw Markdown is readable&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For developers writing documentation, ADRs, or capability matrices, this is essential. Before Advanced Tables, I would export tables from a spreadsheet and paste them in. Now I write them directly.&lt;/p&gt;
&lt;h3 id=&quot;alternative-use-our-table-tools&quot;&gt;Alternative: use our table tools&lt;/h3&gt;
&lt;p&gt;If you&amp;#39;re building a table from data that&amp;#39;s already in a spreadsheet or CSV:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/sheets-to-markdown&quot;&gt;Google Sheets → Markdown&lt;/a&gt; — paste a spreadsheet selection&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/excel-to-markdown&quot;&gt;Excel → Markdown&lt;/a&gt; — upload an .xlsx file&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/csv-to-markdown&quot;&gt;CSV → Markdown&lt;/a&gt; — paste CSV data&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;6-tasks--cross-vault-task-tracking&quot;&gt;6. Tasks — cross-vault task tracking&lt;/h2&gt;
&lt;p&gt;Tasks extends Obsidian&amp;#39;s native &lt;code&gt;- [x]&lt;/code&gt; task syntax with due dates, recurrence rules, priorities, and cross-file queries.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Install:&lt;/strong&gt; Community plugins → search &amp;quot;Tasks&amp;quot;&lt;/p&gt;
&lt;h3 id=&quot;syntax-you-get&quot;&gt;Syntax you get&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; [ ] Write runbook for new deployment process 📅 2026-07-01 ⏫
&lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; [ ] Review PR from @alice 📅 2026-06-28 🔁 every week
&lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; [x] Update dependencies ✅ 2026-06-25
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;dashboard-query&quot;&gt;Dashboard query&lt;/h3&gt;
&lt;p&gt;Put this in a dedicated &lt;code&gt;tasks.md&lt;/code&gt; file to see everything due this week:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;```tasks
not done
due before in 7 days
group by due
sort by priority
```
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For developers who live in Obsidian and want to track project work without switching to Jira/Linear/Trello, Tasks is a solid alternative. The lack of real-time collaboration is the main limitation.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;7-linter--keep-your-markdown-consistent&quot;&gt;7. Linter — keep your Markdown consistent&lt;/h2&gt;
&lt;p&gt;Linter automatically fixes common Markdown style issues on save: trailing spaces, inconsistent heading levels, missing blank lines around headings, duplicate headings, and more.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Install:&lt;/strong&gt; Community plugins → search &amp;quot;Linter&amp;quot;&lt;/p&gt;
&lt;h3 id=&quot;why-this-matters-for-developers&quot;&gt;Why this matters for developers&lt;/h3&gt;
&lt;p&gt;If you publish from Obsidian (to a Jekyll blog, MkDocs site, or GitHub wiki), consistent Markdown saves you debugging time. Malformed tables, missing blank lines, and inconsistent heading hierarchies can break renderers in ways that are hard to diagnose.&lt;/p&gt;
&lt;p&gt;Configure Linter to &lt;strong&gt;auto-fix on save&lt;/strong&gt; (Settings → Linter → Lint on save), and set the rules that matter to your publishing target. For Jekyll/GitHub Pages, I always enable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;YAML frontmatter formatting&lt;/li&gt;
&lt;li&gt;Heading levels increment by one&lt;/li&gt;
&lt;li&gt;No trailing spaces&lt;/li&gt;
&lt;li&gt;Proper blank lines around code blocks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Want to check Markdown quality before pushing? Try our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-linter&quot;&gt;Markdown Linter tool&lt;/a&gt; — same rules, no plugin required.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;honorable-mentions&quot;&gt;Honorable mentions&lt;/h2&gt;
&lt;p&gt;These didn&amp;#39;t make the essential list but are worth knowing about:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Excalidraw&lt;/strong&gt; (&lt;code&gt;excalidraw-plugin&lt;/code&gt;) — embed whiteboard diagrams directly in notes. Useful for architecture sketches that live next to the ADR they explain.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kanban&lt;/strong&gt; (&lt;code&gt;obsidian-kanban&lt;/code&gt;) — Trello-style boards inside Obsidian. Each card is a Markdown note. Good for sprint planning if you prefer staying in Obsidian.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Local REST API&lt;/strong&gt; (&lt;code&gt;obsidian-local-rest-api&lt;/code&gt;) — exposes your vault via a local HTTP API. Lets you build scripts that read/write notes from the command line or automate note creation from external tools.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Shell Commands&lt;/strong&gt; (&lt;code&gt;obsidian-shellcommands&lt;/code&gt;) — run shell commands from Obsidian&amp;#39;s command palette. I use this to open the current note&amp;#39;s folder in Terminal and to run &lt;code&gt;git pull&lt;/code&gt; on demand.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Iconize&lt;/strong&gt; — add emoji or icon prefixes to folder names in the sidebar. Trivial, but makes the vault faster to navigate when you have 20+ folders.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;recommended-vault-structure-for-developers&quot;&gt;Recommended vault structure for developers&lt;/h2&gt;
&lt;p&gt;A folder structure that works well with Dataview:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vault/
├── _templates/          # Templater templates
├── daily/               # Daily notes (YYYY-MM-DD.md)
├── projects/            # One folder per project
│   └── project-name/
│       ├── README.md    # Project overview with frontmatter status
│       ├── decisions/   # ADRs
│       └── retros/      # Incident retrospectives
├── reference/           # Long-lived reference docs
│   ├── runbooks/
│   └── architecture/
├── inbox/               # Quick capture, triage weekly
└── tasks.md             # Dataview task dashboard
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Frontmatter for project notes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;title:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Project&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Alpha&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;status:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;active&lt;/span&gt;   &lt;span class=&quot;hljs-comment&quot;&gt;# active | paused | done&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;stack:&lt;/span&gt; [&lt;span class=&quot;hljs-string&quot;&gt;Node.js&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;PostgreSQL&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;Docker&lt;/span&gt;]
&lt;span class=&quot;hljs-attr&quot;&gt;team:&lt;/span&gt; [&lt;span class=&quot;hljs-string&quot;&gt;alice&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;bob&lt;/span&gt;]
&lt;span class=&quot;hljs-attr&quot;&gt;last-updated:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;2026-06-26&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;With this structure, the Dataview queries from earlier work immediately.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-to-skip&quot;&gt;What to skip&lt;/h2&gt;
&lt;p&gt;A few popular plugins that aren&amp;#39;t worth the friction for a typical developer workflow:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mind Map&lt;/strong&gt; — generating a mind map from a note sounds useful but in practice is never the right tool. Use Excalidraw for diagrams that need real thought.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sliding Panes / Andy&amp;#39;s Mode&lt;/strong&gt; — horizontal scrolling panels instead of tabs. It was useful before Obsidian added proper tab groups; now it&amp;#39;s just weird.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Natural Language Dates&lt;/strong&gt; — parses &amp;quot;next Friday&amp;quot; as a date. The Tasks plugin handles this better with its own date picker.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI writing plugins&lt;/strong&gt; — there are several. Most of them call external APIs, which means your notes leave your machine. If data privacy matters (and for developer notes it usually does), be cautious.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Open Obsidian → Settings → Community plugins → Turn off Restricted Mode&lt;/li&gt;
&lt;li&gt;Browse and install the seven plugins from the list at the top&lt;/li&gt;
&lt;li&gt;Enable Templater&amp;#39;s template folder and set up your first template&lt;/li&gt;
&lt;li&gt;Configure Obsidian Git with your remote repository&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;tasks.md&lt;/code&gt; with a Dataview query and use it as your starting point&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The setup takes about 30 minutes. After that, the vault mostly manages itself.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Related: &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-in-obsidian-complete-workflow-guide&quot;&gt;Markdown in Obsidian: The Complete Workflow Guide&lt;/a&gt; covers the Obsidian-specific Markdown features (wiki links, callouts, embedded notes) that you&amp;#39;ll use alongside these plugins.&lt;/em&gt;&lt;/p&gt;
</content>
  </entry>
  <entry>
    <title>Markdown for API Documentation: OpenAPI, Stoplight, and the Docs-as-Code Stack</title>
    <link href="https://www.markdowntools.com/blog/markdown-for-api-documentation" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/markdown-for-api-documentation</id>
    <published>2026-06-19T00:00:00+00:00</published>
    <updated>2026-06-19T00:00:00+00:00</updated>
    <author><name>Matt Rathbone</name></author>
    <category term="workflows"/>
    <summary>Writing API documentation with Markdown? This guide covers where Markdown lives in OpenAPI specs, how the major platforms handle it, and how to set up a docs-as-code workflow.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/markdown-for-api-documentation">&lt;p&gt;API documentation sits at the intersection of technical writing, software development, and developer experience. It&amp;#39;s also one of the areas where Markdown has quietly become essential — often in ways that aren&amp;#39;t obvious until you&amp;#39;re knee-deep in an OpenAPI spec or fighting a Stoplight rendering issue.&lt;/p&gt;
&lt;p&gt;This guide covers where Markdown appears in API documentation, how the major platforms handle it, how to set up a docs-as-code workflow, and what to watch out for when writing descriptions that survive the journey from spec to rendered docs.&lt;/p&gt;
&lt;h2 id=&quot;where-markdown-lives-in-an-openapi-spec&quot;&gt;Where Markdown Lives in an OpenAPI Spec&lt;/h2&gt;
&lt;p&gt;If you&amp;#39;re working with OpenAPI (formerly Swagger), Markdown appears in several places:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Description fields&lt;/strong&gt; are the most common. Nearly every object in the OpenAPI spec accepts a &lt;code&gt;description&lt;/code&gt; field, and these support CommonMark Markdown:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;info:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;title:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Payments&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;API&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;version:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;quot;2.0&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;|
    The **Payments API** lets you create, capture, and refund charges.
&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;## Authentication&lt;/span&gt;

    &lt;span class=&quot;hljs-string&quot;&gt;All&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;requests&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;require&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Bearer&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;token&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;`Authorization`&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;header.&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;Rate limits apply:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;100&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;requests/second&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;per&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;API&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;key.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;description&lt;/code&gt; field on paths, operations, parameters, request bodies, and response schemas all support Markdown:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;paths:&lt;/span&gt;
  &lt;span class=&quot;hljs-string&quot;&gt;/charges:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;post:&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;summary:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Create&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;charge&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;|
        Creates a new charge. The `amount` is in the **smallest currency unit**
        (e.g., cents for USD, pence for GBP).
&lt;/span&gt;
        &lt;span class=&quot;hljs-string&quot;&gt;See&lt;/span&gt; [&lt;span class=&quot;hljs-string&quot;&gt;currency&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;codes&lt;/span&gt;]&lt;span class=&quot;hljs-string&quot;&gt;(/docs/currencies)&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;full&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;list.&lt;/span&gt;
      &lt;span class=&quot;hljs-attr&quot;&gt;parameters:&lt;/span&gt;
        &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;idempotency_key&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;in:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;header&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;|
            A unique key to safely retry requests. If you send the same key twice,
            the API returns the original response rather than creating a duplicate.
&lt;/span&gt;
            &lt;span class=&quot;hljs-string&quot;&gt;Recommended&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;all&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;`POST`&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;requests.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Markdown guide files&lt;/strong&gt; alongside the spec are the second major use case. Most teams write long-form content — tutorials, quickstart guides, authentication flows — as &lt;code&gt;.md&lt;/code&gt; files that live in the same repository as &lt;code&gt;openapi.yaml&lt;/code&gt;, versioned and reviewed together.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Changelog files&lt;/strong&gt; are almost always Markdown. Your &lt;code&gt;CHANGELOG.md&lt;/code&gt; lives in git alongside the spec and gets rendered by whatever platform you use for documentation.&lt;/p&gt;
&lt;h2 id=&quot;the-api-documentation-platforms-compared&quot;&gt;The API Documentation Platforms Compared&lt;/h2&gt;
&lt;p&gt;Most teams don&amp;#39;t render OpenAPI specs manually — they use a platform that reads the spec and generates documentation. These platforms differ significantly in how they handle Markdown.&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Markdown in descriptions&lt;/th&gt;
&lt;th&gt;Custom Markdown pages&lt;/th&gt;
&lt;th&gt;Self-hostable&lt;/th&gt;
&lt;th&gt;Pricing model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Redocly&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full CommonMark + extensions&lt;/td&gt;
&lt;td&gt;Yes (docs-as-code)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Open-source CLI; hosted plans&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stoplight&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full CommonMark&lt;/td&gt;
&lt;td&gt;Yes (separate &lt;code&gt;.md&lt;/code&gt; files)&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;Freemium SaaS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scalar&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full CommonMark&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (open-source)&lt;/td&gt;
&lt;td&gt;Open-source; hosted option&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Readme.io&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CommonMark + custom blocks&lt;/td&gt;
&lt;td&gt;Yes (guides section)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Commercial SaaS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Swagger UI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited (sanitized HTML)&lt;/td&gt;
&lt;td&gt;No (spec only)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Open-source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RapiDoc&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;CommonMark&lt;/td&gt;
&lt;td&gt;No (spec only)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Open-source&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Redocly&lt;/strong&gt; is the go-to choice for teams who want full docs-as-code. You write OpenAPI + Markdown files, run &lt;code&gt;redocly build-docs&lt;/code&gt;, and get a self-hosted documentation site. Their &lt;code&gt;@redocly/cli&lt;/code&gt; also lints your OpenAPI spec for correctness, consistency, and style. Markdown support is robust: admonitions (callouts), tabs, code blocks with syntax highlighting, and cross-references between guides and reference docs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Stoplight Studio&lt;/strong&gt; is the most popular visual editor for OpenAPI. It renders Markdown in description fields faithfully and lets you write standalone Markdown pages that appear alongside the generated reference docs. The tradeoff: the visual editor can sometimes mangle complex Markdown in descriptions — always verify in preview mode before committing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scalar&lt;/strong&gt; is newer and gaining momentum fast. It&amp;#39;s fully open-source, renders OpenAPI specs with a modern, clean UI, and handles CommonMark properly in all description fields. Worth considering for teams who want something that looks great without vendor lock-in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Readme.io&lt;/strong&gt; is the commercial choice for teams that want a hosted developer portal with analytics, user authentication for private docs, and API key management built in. Their Markdown support includes custom callout blocks and interactive example makers. The tradeoff: more lock-in than the open-source alternatives, and the pricing climbs quickly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Swagger UI&lt;/strong&gt; is the original and still widely deployed — many teams use it for internal reference or expose it directly from their API. Its Markdown support is limited, though: descriptions pass through a sanitized HTML pipeline, and complex Markdown elements don&amp;#39;t always behave as expected. Stick to basic formatting (bold, italic, inline code, links) if Swagger UI is your target.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-docs-as-code-for-api-documentation&quot;&gt;Setting Up Docs-as-Code for API Documentation&lt;/h2&gt;
&lt;p&gt;The docs-as-code approach treats documentation like code: stored in git, reviewed in pull requests, deployed through CI/CD. For API docs, this means your repository looks something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;api-docs/
├── openapi.yaml
├── docs/
│   ├── getting-started.md
│   ├── authentication.md
│   ├── rate-limits.md
│   └── changelog.md
├── redocly.yaml
└── .github/
    └── workflows/
        └── docs.yml
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A minimal GitHub Actions workflow with Redocly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;API&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Docs&lt;/span&gt;

&lt;span class=&quot;hljs-attr&quot;&gt;on:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;push:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;branches:&lt;/span&gt; [&lt;span class=&quot;hljs-string&quot;&gt;main&lt;/span&gt;]
  &lt;span class=&quot;hljs-attr&quot;&gt;pull_request:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;branches:&lt;/span&gt; [&lt;span class=&quot;hljs-string&quot;&gt;main&lt;/span&gt;]

&lt;span class=&quot;hljs-attr&quot;&gt;jobs:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;lint-and-build:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;runs-on:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;steps:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Install&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Redocly&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;CLI&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;-g&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;@redocly/cli&lt;/span&gt;

      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Lint&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;OpenAPI&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;spec&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;redocly&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;lint&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;openapi.yaml&lt;/span&gt;

      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Build&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;docs&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;redocly&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;build-docs&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;openapi.yaml&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--output&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;dist/&lt;/span&gt;

      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Deploy&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;GitHub&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Pages&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;if:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;github.ref&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;refs/heads/main&amp;#x27;&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;peaceiris/actions-gh-pages@v3&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;with:&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;github_token:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;${{&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;secrets.GITHUB_TOKEN&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;}}&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;publish_dir:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;dist/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;redocly lint&lt;/code&gt; step catches spec errors and missing descriptions before they reach production. For Markdown files, adding &lt;a href=&quot;https://github.com/DavidAnson/markdownlint&quot;&gt;markdownlint&lt;/a&gt; to the same workflow catches style issues in your guide files — the same rules you&amp;#39;d run locally with our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-linter&quot;&gt;Markdown Linter&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;writing-good-markdown-api-descriptions&quot;&gt;Writing Good Markdown API Descriptions&lt;/h2&gt;
&lt;p&gt;The descriptions in your OpenAPI spec are often the first thing a developer reads about an endpoint. A few principles that make a meaningful difference:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lead with the &amp;quot;what&amp;quot;, not the &amp;quot;how&amp;quot;.&lt;/strong&gt; The operation name already tells you what it does; the description should explain when to use it and what to watch for.&lt;/p&gt;
&lt;p&gt;Instead of:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Creates&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;the&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;database.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Write:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;|
  Creates a new user account. Returns the created user object including the generated `id`.
&lt;/span&gt;
  &lt;span class=&quot;hljs-string&quot;&gt;**Rate&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;limit:**&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;10&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;requests/minute&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;per&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;IP.&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Creating&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;users&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;bulk?&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Use&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;the&lt;/span&gt;
  [&lt;span class=&quot;hljs-string&quot;&gt;batch&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;endpoint&lt;/span&gt;]&lt;span class=&quot;hljs-string&quot;&gt;(/reference/users/import)&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;instead.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Always show the unit, not just the type.&lt;/strong&gt; For numeric fields, clarify what the number represents:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;parameters:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;amount&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;|
      The charge amount in the **smallest currency unit** for the given currency.
      For USD: `100` = $1.00. For JPY (zero-decimal): `100` = ¥100.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Use code fences for examples.&lt;/strong&gt; Most platforms (Redocly, Stoplight, Scalar) syntax-highlight fenced code blocks in description fields:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;|
  Include a unique idempotency key on every `POST` request:
&lt;/span&gt;
  &lt;span class=&quot;hljs-string&quot;&gt;```&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;X-Idempotency-Key:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;550e8400&lt;/span&gt;&lt;span class=&quot;hljs-string&quot;&gt;-e29b-41d4-a716-446655440000&lt;/span&gt;
  &lt;span class=&quot;hljs-string&quot;&gt;```&lt;/span&gt;

  &lt;span class=&quot;hljs-string&quot;&gt;Keys&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;expire&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;after&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;24&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;hours.&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Reusing&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;an&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;expired&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;key&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;creates&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;request.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note the nested code fences here — a triple-backtick block inside a YAML string. Most OpenAPI renderers handle this correctly; Swagger UI is the common exception.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Link between operations.&lt;/strong&gt; Relative links (&lt;code&gt;[see also](/reference/refunds)&lt;/code&gt;) connect related endpoints and keep users in the docs. Most platforms render these as proper links; the Markdown source stays readable in the raw spec.&lt;/p&gt;
&lt;h2 id=&quot;what-doesnt-work-well&quot;&gt;What Doesn&amp;#39;t Work Well&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Complex tables in Swagger UI.&lt;/strong&gt; Swagger UI renders Markdown tables inconsistently — the cell contents are sometimes sanitized in ways that strip inline formatting. Test tables in your target platform before relying on them for critical information.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nested code blocks.&lt;/strong&gt; Showing a code block example that &lt;em&gt;contains&lt;/em&gt; another code block (meta-documentation) trips up some renderers. If you need to demonstrate nested fences, use an indented code block (4 spaces) for the inner example instead of triple backticks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HTML in descriptions.&lt;/strong&gt; OpenAPI allows HTML in description fields, and CommonMark allows inline HTML in Markdown. But documentation platforms sanitize it — &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt;, and sometimes even &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; get stripped. Don&amp;#39;t rely on raw HTML for anything load-bearing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Relative paths across platforms.&lt;/strong&gt; A link like &lt;code&gt;[authentication](/docs/authentication)&lt;/code&gt; works perfectly when self-hosting with Redocly but breaks in Stoplight if your docs live at a different base path. Always test cross-links in the actual deployed environment.&lt;/p&gt;
&lt;h2 id=&quot;checking-your-markdown-before-it-ships&quot;&gt;Checking Your Markdown Before It Ships&lt;/h2&gt;
&lt;p&gt;Before committing an updated OpenAPI spec or guide file:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Run the platform&amp;#39;s linter.&lt;/strong&gt; &lt;code&gt;redocly lint openapi.yaml&lt;/code&gt; catches spec-level issues; &lt;code&gt;markdownlint&lt;/code&gt; catches Markdown style problems in your &lt;code&gt;.md&lt;/code&gt; files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Preview in the target platform.&lt;/strong&gt; What looks right in VS Code may render differently in Stoplight or Readme.io — especially for tables and nested code blocks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check every code block manually.&lt;/strong&gt; Unterminated fences are the most common rendering failure. They can silently swallow everything below them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Verify all cross-links.&lt;/strong&gt; Use a link-checker in CI for large doc sets; manual spot-checking for smaller ones.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For catching Markdown style issues locally before pushing, the &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-linter&quot;&gt;Markdown Linter&lt;/a&gt; at Markdown Tools checks heading hierarchy, bare URLs, missing alt text, unclosed code blocks, and more. The &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-editor&quot;&gt;Markdown Editor&lt;/a&gt; gives you live preview so you can see how your guide content will look before it hits the platform.&lt;/p&gt;
&lt;h2 id=&quot;platform-support-quick-reference&quot;&gt;Platform Support Quick Reference&lt;/h2&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format&lt;/th&gt;
&lt;th&gt;OpenAPI descriptions&lt;/th&gt;
&lt;th&gt;Guide &lt;code&gt;.md&lt;/code&gt; files&lt;/th&gt;
&lt;th&gt;Swagger UI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Bold, italic&lt;/td&gt;
&lt;td&gt;All platforms&lt;/td&gt;
&lt;td&gt;All platforms&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inline code&lt;/td&gt;
&lt;td&gt;All platforms&lt;/td&gt;
&lt;td&gt;All platforms&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fenced code blocks&lt;/td&gt;
&lt;td&gt;Redocly, Stoplight, Scalar, Readme&lt;/td&gt;
&lt;td&gt;All platforms&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tables&lt;/td&gt;
&lt;td&gt;Redocly, Stoplight, Scalar, Readme&lt;/td&gt;
&lt;td&gt;All platforms&lt;/td&gt;
&lt;td&gt;Inconsistent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Links&lt;/td&gt;
&lt;td&gt;All platforms&lt;/td&gt;
&lt;td&gt;All platforms&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Task lists&lt;/td&gt;
&lt;td&gt;Some&lt;/td&gt;
&lt;td&gt;Redocly, Readme&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Footnotes&lt;/td&gt;
&lt;td&gt;Some&lt;/td&gt;
&lt;td&gt;Redocly (MDX), Readme&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Admonitions / callouts&lt;/td&gt;
&lt;td&gt;Redocly, Readme (custom blocks)&lt;/td&gt;
&lt;td&gt;Redocly, Readme&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raw HTML&lt;/td&gt;
&lt;td&gt;Varies — most sanitize&lt;/td&gt;
&lt;td&gt;Varies&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;related-resources&quot;&gt;Related Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/markdown-linter&quot;&gt;Markdown Linter&lt;/a&gt; — catch style issues before committing&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/markdown-editor&quot;&gt;Markdown Editor&lt;/a&gt; — live preview while drafting guide content&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-html&quot;&gt;Markdown to HTML&lt;/a&gt; — export clean HTML for platforms that accept HTML&lt;/li&gt;
&lt;li&gt;Our guide: &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-for-technical-writers-docs-as-code&quot;&gt;Markdown for Technical Writers: The Docs-as-Code Workflow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Our guide: &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-syntax-highlighting-complete-guide&quot;&gt;Markdown Syntax Highlighting&lt;/a&gt; — getting language tags right in code blocks&lt;/li&gt;
&lt;li&gt;Our guide: &lt;a href=&quot;https://www.markdowntools.com/blog/mkdocs-vs-docusaurus-vs-gitbook&quot;&gt;MkDocs vs Docusaurus vs GitBook&lt;/a&gt; — choosing the right docs platform&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Markdown to Jira Wiki Markup: The Complete Conversion Guide</title>
    <link href="https://www.markdowntools.com/blog/markdown-to-jira-wiki-markup-guide" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/markdown-to-jira-wiki-markup-guide</id>
    <published>2026-06-19T00:00:00+00:00</published>
    <updated>2026-06-19T00:00:00+00:00</updated>
    <author><name>Matt Rathbone</name></author>
    <category term="workflows"/>
    <summary>Converting Markdown to Jira wiki markup? This guide covers every syntax difference, explains how Jira Cloud vs Server handle formatting, and shows you how to automate the conversion.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/markdown-to-jira-wiki-markup-guide">&lt;p&gt;If you write documentation in Markdown but need to post it to Jira, you&amp;#39;ve hit the wall: Jira has its own wiki markup language, and pasting Markdown directly doesn&amp;#39;t work — or doesn&amp;#39;t work reliably. The results depend on which version of Jira you&amp;#39;re using, which project type, and whether your admin has enabled anything.&lt;/p&gt;
&lt;p&gt;This guide maps out every syntax difference, explains what actually works in each Jira context, and shows you how to do the conversion reliably.&lt;/p&gt;
&lt;h2 id=&quot;why-jira-doesnt-just-support-markdown&quot;&gt;Why Jira Doesn&amp;#39;t Just Support Markdown&lt;/h2&gt;
&lt;p&gt;Jira&amp;#39;s text formatting language (wiki markup) predates widespread Markdown adoption. It uses its own syntax that&amp;#39;s close to — but not the same as — Markdown. In Cloud and Server, Atlassian has gradually added Markdown-like support, but it&amp;#39;s incomplete and inconsistent across project types.&lt;/p&gt;
&lt;p&gt;The result: the same pasted content renders differently in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Jira Data Center/Server&lt;/strong&gt; — pure wiki markup, very little native Markdown support&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jira Cloud (classic projects)&lt;/strong&gt; — hybrid editor, some Markdown autocomplete on the Visual side&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jira Cloud (next-gen / team-managed projects)&lt;/strong&gt; — rich text editor, accepts some Markdown on paste, no wiki markup at all&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This distinction matters more than anything else in this guide.&lt;/p&gt;
&lt;h2 id=&quot;which-jira-version-are-you-using&quot;&gt;Which Jira Version Are You Using?&lt;/h2&gt;
&lt;p&gt;Before converting anything, identify your environment:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Jira type&lt;/th&gt;
&lt;th&gt;How to tell&lt;/th&gt;
&lt;th&gt;Markdown support&lt;/th&gt;
&lt;th&gt;Wiki markup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Data Center / Server&lt;/td&gt;
&lt;td&gt;Self-hosted, URL isn&amp;#39;t atlassian.net&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud — classic project&lt;/td&gt;
&lt;td&gt;atlassian.net domain, Text/Visual editor toggle&lt;/td&gt;
&lt;td&gt;Partial (autocomplete)&lt;/td&gt;
&lt;td&gt;Full&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud — next-gen project&lt;/td&gt;
&lt;td&gt;atlassian.net, no &amp;quot;Text&amp;quot; tab in editor&lt;/td&gt;
&lt;td&gt;Paste-in only&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;For Data Center/Server and Cloud classic&lt;/strong&gt;: Convert to wiki markup before pasting.&lt;br&gt;&lt;strong&gt;For Cloud next-gen&lt;/strong&gt;: Paste Markdown directly into the rich text editor; basic formatting survives (bold, italic, lists), but tables and code blocks usually break.&lt;/p&gt;
&lt;h2 id=&quot;the-complete-markdown--jira-wiki-markup-conversion-table&quot;&gt;The Complete Markdown → Jira Wiki Markup Conversion Table&lt;/h2&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Element&lt;/th&gt;
&lt;th&gt;Markdown&lt;/th&gt;
&lt;th&gt;Jira Wiki Markup&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Heading 1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;# Heading&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;h1. Heading&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heading 2&lt;/td&gt;
&lt;td&gt;&lt;code&gt;## Heading&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;h2. Heading&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heading 3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;### Heading&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;h3. Heading&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bold&lt;/td&gt;
&lt;td&gt;&lt;code&gt;**text**&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;*text*&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Italic&lt;/td&gt;
&lt;td&gt;&lt;code&gt;*text*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;_text_&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strikethrough&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~~text~~&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-text-&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inline code&lt;/td&gt;
&lt;td&gt;&lt;code&gt;`code`&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{{code}}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code block&lt;/td&gt;
&lt;td&gt;&lt;code&gt;```lang&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{code:lang}...{code}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blockquote&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&amp;gt; text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{quote}text{quote}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Link&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[text](url)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[text|url]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image&lt;/td&gt;
&lt;td&gt;&lt;code&gt;![alt](url)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;!url!&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unordered list&lt;/td&gt;
&lt;td&gt;&lt;code&gt;- item&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;* item&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ordered list&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1. item&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;# item&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Horizontal rule&lt;/td&gt;
&lt;td&gt;&lt;code&gt;---&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;----&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Table header&lt;/td&gt;
&lt;td&gt;&lt;code&gt;| head |&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;|| head ||&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Table cell&lt;/td&gt;
&lt;td&gt;&lt;code&gt;| cell |&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;| cell |&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;The differences look subtle but they trip people up constantly — especially &lt;code&gt;*bold*&lt;/code&gt; becoming &lt;code&gt;_italic_&lt;/code&gt; in Jira (the opposite of how Markdown works).&lt;/p&gt;
&lt;h2 id=&quot;converting-code-blocks&quot;&gt;Converting Code Blocks&lt;/h2&gt;
&lt;p&gt;This is where things get interesting. Jira uses macro syntax for code blocks:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Markdown:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;```javascript
const greeting = &amp;#39;Hello, world!&amp;#39;;
console.log(greeting);
```
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Jira wiki markup:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{code:javascript}
const greeting = &amp;#39;Hello, world!&amp;#39;;
console.log(greeting);
{code}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The language name goes as a parameter to the &lt;code&gt;{code}&lt;/code&gt; macro. Supported languages include &lt;code&gt;java&lt;/code&gt;, &lt;code&gt;javascript&lt;/code&gt;, &lt;code&gt;python&lt;/code&gt;, &lt;code&gt;sql&lt;/code&gt;, &lt;code&gt;xml&lt;/code&gt;, &lt;code&gt;html&lt;/code&gt;, &lt;code&gt;css&lt;/code&gt;, &lt;code&gt;bash&lt;/code&gt;, &lt;code&gt;ruby&lt;/code&gt;, &lt;code&gt;php&lt;/code&gt;, and dozens more. If you omit the language, the block renders as plain preformatted text.&lt;/p&gt;
&lt;p&gt;For inline code, Markdown uses backticks while Jira uses &lt;code&gt;{{double curly braces}}&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Markdown&lt;/th&gt;
&lt;th&gt;Jira&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;`variable`&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{{variable}}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;`npm install`&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{{npm install}}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;h2 id=&quot;converting-tables&quot;&gt;Converting Tables&lt;/h2&gt;
&lt;p&gt;Tables are one of the clearest differences. Markdown uses a &lt;code&gt;---&lt;/code&gt; separator row to mark headers; Jira uses double pipes (&lt;code&gt;||&lt;/code&gt;) for header cells and single pipes (&lt;code&gt;|&lt;/code&gt;) for body cells.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Markdown:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;| Name | Role | Access |
|------|------|--------|
| Alice | Admin | Full |
| Bob | Developer | Read/Write |
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Jira wiki markup:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;|| Name || Role || Access ||
| Alice | Admin | Full |
| Bob | Developer | Read/Write |
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There&amp;#39;s no separator row in Jira. The header is inferred purely from whether the row uses &lt;code&gt;||&lt;/code&gt; or &lt;code&gt;|&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;What Jira wiki markup tables &lt;strong&gt;don&amp;#39;t&lt;/strong&gt; support:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Column alignment (&lt;code&gt;:---:&lt;/code&gt; / &lt;code&gt;---:&lt;/code&gt; syntax is ignored)&lt;/li&gt;
&lt;li&gt;Multi-line cells&lt;/li&gt;
&lt;li&gt;Colspan or rowspan (requires the HTML macro)&lt;/li&gt;
&lt;li&gt;Cell background colours (without the HTML macro)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you need any of those, convert to HTML using our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-html&quot;&gt;Markdown to HTML tool&lt;/a&gt; and paste using Jira&amp;#39;s HTML macro.&lt;/p&gt;
&lt;h2 id=&quot;converting-lists-and-nesting&quot;&gt;Converting Lists and Nesting&lt;/h2&gt;
&lt;p&gt;Both Markdown and Jira use &lt;code&gt;*&lt;/code&gt; and &lt;code&gt;#&lt;/code&gt; for lists, but Jira doesn&amp;#39;t use indentation for nesting — it uses repeated characters:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Markdown (nested):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; Item 1
&lt;span class=&quot;hljs-bullet&quot;&gt;  -&lt;/span&gt; Nested item
&lt;span class=&quot;hljs-bullet&quot;&gt;  -&lt;/span&gt; Another nested
&lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; Item 2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Jira wiki markup (nested):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;* Item 1
** Nested item
** Another nested
* Item 2
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Each additional level of nesting repeats the character. Two levels deep is &lt;code&gt;**&lt;/code&gt;, three is &lt;code&gt;***&lt;/code&gt;. Indentation in Markdown doesn&amp;#39;t translate automatically — you need to count nesting depth and repeat accordingly.&lt;/p&gt;
&lt;p&gt;Mixed ordered/unordered nested lists work the same way:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;* First level unordered
*# Second level ordered under unordered
*#* Third level unordered under ordered under unordered
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;converting-blockquotes&quot;&gt;Converting Blockquotes&lt;/h2&gt;
&lt;p&gt;Markdown blockquotes use &lt;code&gt;&amp;gt;&lt;/code&gt; prefixes. Jira uses a &lt;code&gt;{quote}&lt;/code&gt; macro:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Markdown:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; This is a quoted passage from the requirements document.&lt;/span&gt;
&lt;span class=&quot;hljs-quote&quot;&gt;&amp;gt; It can span multiple lines.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Jira:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{quote}
This is a quoted passage from the requirements document.
It can span multiple lines.
{quote}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Nested blockquotes (&lt;code&gt;&amp;gt; &amp;gt; text&lt;/code&gt;) aren&amp;#39;t directly supported in Jira wiki markup — you get one level.&lt;/p&gt;
&lt;h2 id=&quot;jira-only-macros&quot;&gt;Jira-Only Macros&lt;/h2&gt;
&lt;p&gt;Wiki markup has macros that have no Markdown equivalent. Knowing these is useful when you&amp;#39;re writing content that will only ever live in Jira:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Colour text:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{color:red}This text is red.{color}
{color:#0052CC}This text is Jira blue.{color}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Info/note/warning/tip panels:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{info}This is an informational note.{info}
{warning}Caution: this action cannot be undone.{warning}
{note}Note: configuration required.{note}
{tip}Pro tip: use keyboard shortcuts.{tip}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Panel with title and styling:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{panel:title=Important|borderColor=#ddd|titleBGColor=#f0f0f0}
Panel content here.
{panel}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;No-format block&lt;/strong&gt; (preformatted text, no syntax highlighting):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{noformat}
Plain text that should appear as-is.
Stack traces, log output, etc.
{noformat}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Jira issue link:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{jira:KEY-123}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;These macros render as styled components in Jira but have no Markdown equivalent. If you&amp;#39;re round-tripping content (Jira → Markdown → Jira), you&amp;#39;ll lose these macros on the Markdown side.&lt;/p&gt;
&lt;h2 id=&quot;what-doesnt-convert-cleanly&quot;&gt;What Doesn&amp;#39;t Convert Cleanly&lt;/h2&gt;
&lt;p&gt;Some Markdown features have no wiki markup equivalent:&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Markdown feature&lt;/th&gt;
&lt;th&gt;In Jira&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Footnotes &lt;code&gt;[^1]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Definition lists&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Task lists &lt;code&gt;- [x]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;(/)&lt;/code&gt; and &lt;code&gt;(x)&lt;/code&gt; emoji for checkmarks; not linked to issue checklists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom heading IDs &lt;code&gt;{#anchor}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strikethrough in tables&lt;/td&gt;
&lt;td&gt;Works, but renders inconsistently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTML elements&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;{html}&lt;/code&gt; macro (Cloud only, off by default)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fenced code inside blockquotes&lt;/td&gt;
&lt;td&gt;Loses code formatting&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;For task lists specifically: Jira has a built-in checklist feature in Cloud, but it&amp;#39;s separate from the text editor and not accessible via wiki markup.&lt;/p&gt;
&lt;h2 id=&quot;using-our-markdown-to-jira-converter&quot;&gt;Using Our Markdown to Jira Converter&lt;/h2&gt;
&lt;p&gt;The fastest way to convert is with our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-jira&quot;&gt;Markdown to Jira tool&lt;/a&gt;. Paste your Markdown on the left, get Jira wiki markup on the right in real time.&lt;/p&gt;
&lt;p&gt;The converter handles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All heading levels (h1–h6)&lt;/li&gt;
&lt;li&gt;Bold, italic, strikethrough&lt;/li&gt;
&lt;li&gt;Inline code and fenced code blocks with language tags preserved&lt;/li&gt;
&lt;li&gt;Tables with header row conversion (&lt;code&gt;||&lt;/code&gt; vs &lt;code&gt;|&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Ordered and unordered lists with nesting depth&lt;/li&gt;
&lt;li&gt;Blockquotes&lt;/li&gt;
&lt;li&gt;Links and images&lt;/li&gt;
&lt;li&gt;Horizontal rules&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&amp;#39;s particularly useful when you have long documentation pages to migrate, or you&amp;#39;re regularly posting Markdown from a blog or docs tool into Jira issues.&lt;/p&gt;
&lt;h2 id=&quot;which-editor-mode-to-use&quot;&gt;Which Editor Mode to Use&lt;/h2&gt;
&lt;p&gt;For &lt;strong&gt;Data Center/Server&lt;/strong&gt;: always use wiki markup. It&amp;#39;s the native format. Click the &lt;code&gt;Text&lt;/code&gt; tab in the issue editor, paste wiki markup, done.&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;Cloud classic projects&lt;/strong&gt;: the &lt;code&gt;Text&lt;/code&gt; tab accepts wiki markup directly. The &lt;code&gt;Visual&lt;/code&gt; tab is WYSIWYG. If you&amp;#39;re using the REST API to create or update issues, the &lt;code&gt;description&lt;/code&gt; field accepts wiki markup for classic projects.&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;Cloud next-gen (team-managed) projects&lt;/strong&gt;: there&amp;#39;s no &lt;code&gt;Text&lt;/code&gt; tab. The editor is rich text only. For basic formatting, paste Markdown and let it auto-convert. For tables and code blocks, paste HTML using your browser&amp;#39;s paste shortcut after converting with our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-html&quot;&gt;Markdown to HTML tool&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For &lt;strong&gt;Jira Cloud API (creating issues programmatically)&lt;/strong&gt;: Cloud&amp;#39;s REST API v3 uses Atlassian Document Format (ADF), not wiki markup. You can&amp;#39;t paste wiki markup into the &lt;code&gt;description&lt;/code&gt; field via the API on Cloud — you need to build an ADF document. Wiki markup is only supported in the Server/DC REST API. This is a significant pain point for teams automating Jira content creation.&lt;/p&gt;
&lt;h2 id=&quot;jira-wiki-markup-reference-cheat-sheet&quot;&gt;Jira Wiki Markup Reference Cheat Sheet&lt;/h2&gt;
&lt;p&gt;Quick reference for the conversions you&amp;#39;ll use most often:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Text formatting:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;*bold*           _italic_         -strikethrough-
+underline+      ^superscript^    ~subscript~
??citation??     {color:red}text{color}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Structure:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;h1. Heading 1          h2. Heading 2          h3. Heading 3
----                   (horizontal rule)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Lists:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;* Bullet             # Numbered
** Nested bullet     ## Nested numbered
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Links and media:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Link text|https://example.com]
[Page title|PAGE TITLE]     (Confluence-style, not standard Jira)
!image.png!                 !image.png|thumbnail!
^file.pdf                   (attachment link)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Macros:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{code:java}code here{code}
{{inline code}}
{quote}quoted text{quote}
{noformat}plain text{noformat}
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&quot;related-posts-and-tools&quot;&gt;Related Posts and Tools&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-jira&quot;&gt;Markdown to Jira converter&lt;/a&gt; — real-time conversion, no signup required&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-confluence&quot;&gt;Markdown to Confluence&lt;/a&gt; — Confluence uses similar wiki markup; see our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-in-confluence-what-works&quot;&gt;Confluence guide&lt;/a&gt; for the differences&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-html&quot;&gt;Markdown to HTML&lt;/a&gt; — for Jira Cloud next-gen where wiki markup isn&amp;#39;t available&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-platform-compatibility-guide&quot;&gt;Markdown Across Platforms&lt;/a&gt; — how Markdown behaves in each major platform&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-for-technical-writers-docs-as-code&quot;&gt;Markdown for Technical Writers&lt;/a&gt; — if you&amp;#39;re managing docs alongside Jira tickets&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Why Your Markdown Isn't Rendering: A Troubleshooting Guide</title>
    <link href="https://www.markdowntools.com/blog/why-your-markdown-isnt-rendering" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/why-your-markdown-isnt-rendering</id>
    <published>2026-06-12T00:00:00+00:00</published>
    <updated>2026-06-12T00:00:00+00:00</updated>
    <author><name>Matt Rathbone</name></author>
    <category term="troubleshooting"/>
    <summary>Markdown looks fine in your editor but renders wrong in production? This guide walks through the most common causes by platform — with specific fixes for each.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/why-your-markdown-isnt-rendering">&lt;p&gt;You write clean Markdown, hit preview, and something goes wrong. The heading doesn&amp;#39;t appear. The code block is mangled. The table renders as pipe characters scattered across a paragraph. The bold text shows up as literal asterisks.&lt;/p&gt;
&lt;p&gt;This isn&amp;#39;t a Markdown problem — it&amp;#39;s almost always a renderer problem. Markdown isn&amp;#39;t one thing; it&amp;#39;s a family of related syntaxes interpreted differently by dozens of parsers. The same document can render perfectly in VS Code, break in Confluence, and look different again on GitHub. Knowing &lt;em&gt;which&lt;/em&gt; renderer you&amp;#39;re debugging is the key to solving the problem fast.&lt;/p&gt;
&lt;p&gt;This guide covers the most common causes, organized by symptom and by platform.&lt;/p&gt;
&lt;h2 id=&quot;start-here-identify-your-renderer&quot;&gt;Start Here: Identify Your Renderer&lt;/h2&gt;
&lt;p&gt;Before anything else, answer this question: &lt;strong&gt;what is rendering your Markdown?&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Where you&amp;#39;re writing&lt;/th&gt;
&lt;th&gt;Renderer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;GitHub README / PR description&lt;/td&gt;
&lt;td&gt;GitHub Flavored Markdown (GFM)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Jekyll site&lt;/td&gt;
&lt;td&gt;kramdown (usually)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hugo site&lt;/td&gt;
&lt;td&gt;Goldmark&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Docusaurus / MDX&lt;/td&gt;
&lt;td&gt;remark&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VS Code preview&lt;/td&gt;
&lt;td&gt;Markdown-it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confluence&lt;/td&gt;
&lt;td&gt;Not really Markdown — XHTML-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slack&lt;/td&gt;
&lt;td&gt;mrkdwn (Slack&amp;#39;s own format)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Obsidian&lt;/td&gt;
&lt;td&gt;Markdown-it with plugins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reddit&lt;/td&gt;
&lt;td&gt;Snoomark (new) or older Reddit Markdown&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notion&lt;/td&gt;
&lt;td&gt;Notion&amp;#39;s own parser (paste-only)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;Each renderer supports a different subset of Markdown features. Most support CommonMark as a baseline; the extended features (tables, task lists, footnotes, strikethrough) vary.&lt;/p&gt;
&lt;h2 id=&quot;the-most-common-causes&quot;&gt;The Most Common Causes&lt;/h2&gt;
&lt;h3 id=&quot;1-the-renderer-doesnt-support-that-syntax&quot;&gt;1. The renderer doesn&amp;#39;t support that syntax&lt;/h3&gt;
&lt;p&gt;This is the most frequent cause. Something looks like standard Markdown but isn&amp;#39;t supported by your target renderer.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Footnotes&lt;/strong&gt; — CommonMark doesn&amp;#39;t include them. They work in Pandoc, kramdown, and GitHub (for Wikis), but not in basic GFM for README files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Definition lists&lt;/strong&gt; — supported by kramdown, not by most others.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom heading IDs&lt;/strong&gt; (&lt;code&gt;## Heading {#my-id}&lt;/code&gt;) — supported by Pandoc and some Hugo themes, not in GitHub Flavored Markdown.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Subscript/superscript&lt;/strong&gt; (&lt;code&gt;H~2~O&lt;/code&gt;, &lt;code&gt;x^2^&lt;/code&gt;) — Pandoc and some plugins only.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Check the renderer&amp;#39;s documentation for which extensions it supports. Don&amp;#39;t assume something works because it works in your local editor.&lt;/p&gt;
&lt;h3 id=&quot;2-missing-blank-lines-around-block-elements&quot;&gt;2. Missing blank lines around block elements&lt;/h3&gt;
&lt;p&gt;Markdown parsers are pickier than you&amp;#39;d expect about blank lines. Most renderers require a blank line before and after:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Block-level HTML tags&lt;/li&gt;
&lt;li&gt;Fenced code blocks&lt;/li&gt;
&lt;li&gt;Lists that follow a paragraph&lt;/li&gt;
&lt;li&gt;Headings that follow content&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Without the blank line, a renderer may treat the content as a paragraph continuation instead of a new block.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This won&amp;#39;t render correctly:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;Some text.
&lt;span class=&quot;hljs-code&quot;&gt;```code
example&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;More text.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
**This will:**
```markdown
Some text.

```code
example
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;More text.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
### 3. Indentation inconsistency in lists

List item continuation and nested lists depend on consistent indentation. The rules vary by renderer:

- CommonMark requires **2 spaces** of indentation for nested items
- Some older parsers require **4 spaces** or a tab
- Mixing tabs and spaces causes unpredictable behaviour

**Symptom:** A nested list renders flat, or a code block inside a list item renders as a paragraph.

**Fix:** Use consistent 2-space indentation throughout, and never mix tabs with spaces.

### 4. HTML inside Markdown getting stripped

Many renderers strip raw HTML for security reasons. This is intentional.

- **GitHub** strips `&amp;lt;style&amp;gt;`, `&amp;lt;script&amp;gt;`, and most event handlers from README files. It keeps safe tags like `&amp;lt;details&amp;gt;`, `&amp;lt;summary&amp;gt;`, `&amp;lt;kbd&amp;gt;`, and `&amp;lt;br&amp;gt;`.
- **Hugo&amp;#39;s Goldmark** strips raw HTML by default. You must add `markup.goldmark.renderer.unsafe: true` to your config to allow it.
- **Confluence** rejects HTML entirely unless you use the dedicated HTML Macro.
- **Docusaurus/MDX** interprets HTML as JSX — missing a closing tag or a non-void self-closing tag will break the entire page.

**Symptom:** Your carefully crafted HTML layout renders as plain text, or throws an error.

**Fix:** Check your renderer&amp;#39;s HTML-in-Markdown policy. For Hugo, enable unsafe rendering. For GitHub, use only the subset of HTML tags that GFM allows. For Confluence, use the HTML Macro.

### 5. Jekyll: Liquid syntax in code blocks


Jekyll processes Liquid template tags *before* Markdown rendering. This means any `{% ... %}` or `{{ ... }}` inside a code block — even a fenced one — will be executed as a template, not displayed as code.


**Symptom:** Code block content disappears, or you see a Liquid error like `Liquid Warning: Liquid syntax error`.

**Fix:** Wrap the problematic code block in `{% raw %}` and `{% endraw %}` tags. Your file should look like this:


```bash
echo &amp;quot;Hello {{ name }}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is required any time your code block contains &lt;code&gt;{{&lt;/code&gt;, &lt;code&gt;}}&lt;/code&gt;, &lt;code&gt;{%&lt;/code&gt;, or &lt;code&gt;%}&lt;/code&gt; — common when documenting Liquid, Jinja2, Handlebars, or Hugo templates.&lt;/p&gt;
&lt;h3 id=&quot;6-jekyll-posts-not-appearing-date-in-the-future&quot;&gt;6. Jekyll: Posts not appearing (date in the future)&lt;/h3&gt;
&lt;p&gt;Jekyll doesn&amp;#39;t render posts with a date in the future by default.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; A post exists in &lt;code&gt;_posts/&lt;/code&gt; but doesn&amp;#39;t appear on the site.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Either set the date to today or earlier in the frontmatter, or add &lt;code&gt;future: true&lt;/code&gt; to &lt;code&gt;_config.yml&lt;/code&gt; in your development environment. Don&amp;#39;t add it to production unless you intend posts to publish early.&lt;/p&gt;
&lt;h3 id=&quot;7-hugo-shortcodes-in-markdown-code-blocks&quot;&gt;7. Hugo: Shortcodes in Markdown code blocks&lt;/h3&gt;
&lt;p&gt;Hugo processes shortcodes (&lt;code&gt;{{&amp;lt; ... &amp;gt;}}&lt;/code&gt;) before rendering, same as Jekyll&amp;#39;s Liquid problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; A code block showing how to use a Hugo shortcode is broken or missing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Use Hugo&amp;#39;s &lt;code&gt;{{&amp;lt;/* ... */&amp;gt;}}&lt;/code&gt; comment syntax inside shortcode examples, or wrap in a highlight shortcode instead of a fenced block:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{{&amp;lt;/* warning */&amp;gt;}}
This is a warning box.
{{&amp;lt;/* /warning */&amp;gt;}}
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;8-tables-missing-separator-row&quot;&gt;8. Tables: Missing separator row&lt;/h3&gt;
&lt;p&gt;A Markdown table requires a separator row of dashes between the header and the body rows. Without it, most renderers won&amp;#39;t recognise it as a table at all.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This won&amp;#39;t render as a table:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;| Name | Role |
| Alice | Admin |
| Bob | Editor |
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;This will:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;| Name | Role |
|------|------|
| Alice | Admin |
| Bob | Editor |
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The number of dashes doesn&amp;#39;t matter (minimum 1 per cell), but the separator row is required.&lt;/p&gt;
&lt;h3 id=&quot;9-confluence-markdown-paste-doesnt-work-as-expected&quot;&gt;9. Confluence: Markdown paste doesn&amp;#39;t work as expected&lt;/h3&gt;
&lt;p&gt;Confluence is not a Markdown-native tool. Its internal format is XHTML. When you paste Markdown, Confluence converts it — and the conversion is lossy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What works on paste:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Basic headings, bold, italic&lt;/li&gt;
&lt;li&gt;Simple unordered and ordered lists&lt;/li&gt;
&lt;li&gt;Code blocks (language tag is often dropped)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;What breaks:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tables (often rendered as plain text)&lt;/li&gt;
&lt;li&gt;Task lists&lt;/li&gt;
&lt;li&gt;Footnotes&lt;/li&gt;
&lt;li&gt;Nested blockquotes&lt;/li&gt;
&lt;li&gt;Raw HTML&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Better approach:&lt;/strong&gt; Use the &lt;code&gt;/markdown&lt;/code&gt; slash command (Confluence Cloud) or the Wiki Markup import path. For reliable table conversion, use our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-confluence&quot;&gt;Markdown to Confluence tool&lt;/a&gt; to convert to wiki markup first, then paste using Insert → Wiki Markup.&lt;/p&gt;
&lt;p&gt;See our full guide: &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-in-confluence-what-works&quot;&gt;Markdown in Confluence: What Actually Works&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;10-github-gfm-vs-standard-markdown&quot;&gt;10. GitHub: GFM vs standard Markdown&lt;/h3&gt;
&lt;p&gt;GitHub Flavored Markdown adds extensions on top of CommonMark, but it also restricts some things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Things that work in GFM but not standard Markdown:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tables (pipe syntax)&lt;/li&gt;
&lt;li&gt;Task lists (&lt;code&gt;- [x]&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Strikethrough (&lt;code&gt;~~text~~&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Autolinks (&lt;code&gt;https://...&lt;/code&gt; without brackets)&lt;/li&gt;
&lt;li&gt;Alerts / callouts (&lt;code&gt;&amp;gt; [!NOTE]&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Things that do NOT work in GitHub README files:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Footnotes (they work in GitHub Wikis and GitHub Pages, but not README.md)&lt;/li&gt;
&lt;li&gt;Definition lists&lt;/li&gt;
&lt;li&gt;Custom CSS or &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; tags&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Symptom:&lt;/strong&gt; A footnote &lt;code&gt;[^1]&lt;/code&gt; renders as literal text in a README but works fine in your local Markdown editor.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Check the &lt;a href=&quot;https://github.github.com/gfm/&quot;&gt;GitHub Flavored Markdown spec&lt;/a&gt; for what&amp;#39;s in-scope. For README files specifically, if a feature isn&amp;#39;t listed there, it won&amp;#39;t work.&lt;/p&gt;
&lt;h2 id=&quot;platform-comparison-what-renders-where&quot;&gt;Platform Comparison: What Renders Where&lt;/h2&gt;
&lt;div class=&quot;table-responsive&quot;&gt;&lt;table class=&quot;table&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;GitHub README&lt;/th&gt;
&lt;th&gt;Jekyll (kramdown)&lt;/th&gt;
&lt;th&gt;Hugo (Goldmark)&lt;/th&gt;
&lt;th&gt;Obsidian&lt;/th&gt;
&lt;th&gt;Confluence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Tables&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️ partial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Task lists&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ plugin&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Footnotes&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strikethrough&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Definition lists&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Raw HTML&lt;/td&gt;
&lt;td&gt;⚠️ safe subset&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️ opt-in&lt;/td&gt;
&lt;td&gt;⚠️&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Math (LaTeX)&lt;/td&gt;
&lt;td&gt;✅ (2022+)&lt;/td&gt;
&lt;td&gt;✅ plugin&lt;/td&gt;
&lt;td&gt;✅ plugin&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mermaid diagrams&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅ plugin&lt;/td&gt;
&lt;td&gt;✅ plugin&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom heading IDs&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;
&lt;p&gt;See our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-platform-compatibility-guide&quot;&gt;full platform compatibility guide&lt;/a&gt; for a more detailed breakdown.&lt;/p&gt;
&lt;h2 id=&quot;debugging-workflow&quot;&gt;Debugging Workflow&lt;/h2&gt;
&lt;p&gt;When your Markdown breaks, work through this checklist:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Isolate the problem&lt;/strong&gt; — create a minimal reproduction. Copy just the broken section into a fresh file. If that renders correctly, the issue might be something earlier in the document (unclosed code block, broken table, etc.) affecting parsing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check for unclosed code blocks&lt;/strong&gt; — an unterminated fenced code block (missing closing &lt;code&gt;) will cause everything after it to render as code. Search for lone &lt;/code&gt; in your document.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check the raw output&lt;/strong&gt; — inspect the HTML source of the rendered page (right-click → View Source). This tells you whether the renderer is producing output at all or silently failing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test in a known-good renderer&lt;/strong&gt; — paste the Markdown into our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-editor&quot;&gt;live editor&lt;/a&gt; to see if it renders there. If it does, the issue is your target renderer, not the Markdown itself.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check your linter&lt;/strong&gt; — use our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-linter&quot;&gt;Markdown Linter&lt;/a&gt; to catch structural issues (skipped heading levels, unclosed blocks, missing alt text) before they cause render failures.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Read the error logs&lt;/strong&gt; — Jekyll and Hugo both print build warnings to the terminal. A Liquid error, a missing file, or a frontmatter parse failure will appear there before you spend 30 minutes debugging Markdown.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;Most Markdown rendering failures fall into one of these categories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Renderer doesn&amp;#39;t support the syntax&lt;/strong&gt; — check the spec for your specific tool&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Missing blank lines&lt;/strong&gt; around block elements&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Template tags in code blocks&lt;/strong&gt; — use &lt;code&gt;{% raw %}&lt;/code&gt; in Jekyll, comment syntax in Hugo&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HTML being stripped&lt;/strong&gt; — check security settings, use only allowed tags&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Confluence&lt;/strong&gt; — it&amp;#39;s not a Markdown renderer; use our wiki markup converter&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Table missing separator row&lt;/strong&gt; — always required&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The fix is almost always straightforward once you know which renderer is involved. Start there, and the rest usually follows.&lt;/p&gt;
&lt;h2 id=&quot;related-posts-and-tools&quot;&gt;Related Posts and Tools&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-platform-compatibility-guide&quot;&gt;Markdown Across Platforms: What Works in GitHub, Notion, Obsidian, and Confluence&lt;/a&gt; — the reference guide for cross-platform compatibility&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-in-confluence-what-works&quot;&gt;Markdown in Confluence: What Actually Works&lt;/a&gt; — deep dive on Confluence specifically&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-for-technical-writers-docs-as-code&quot;&gt;Markdown for Technical Writers: The Docs-as-Code Workflow&lt;/a&gt; — setting up markdownlint and Vale in CI&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/markdown-linter&quot;&gt;Markdown Linter&lt;/a&gt; — check your Markdown for style issues instantly&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/markdown-editor&quot;&gt;Markdown Live Editor&lt;/a&gt; — test how your Markdown renders in real time&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/tools/markdown-to-confluence&quot;&gt;Markdown to Confluence&lt;/a&gt; — convert Markdown to Confluence wiki markup&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
  <entry>
    <title>Markdown for Technical Writers: The Docs-as-Code Workflow Guide</title>
    <link href="https://www.markdowntools.com/blog/markdown-for-technical-writers-docs-as-code" rel="alternate" type="text/html"/>
    <id>https://www.markdowntools.com/blog/markdown-for-technical-writers-docs-as-code</id>
    <published>2026-05-15T00:00:00+00:00</published>
    <updated>2026-05-15T00:00:00+00:00</updated>
    <author><name>Matt Rathbone</name></author>
    <category term="workflows"/>
    <summary>How to set up a docs-as-code workflow with Markdown, Git, and a static site generator — practical guidance for technical writers moving away from wiki-based tools.</summary>
    <content type="html" xml:base="https://www.markdowntools.com/blog/markdown-for-technical-writers-docs-as-code">&lt;p&gt;&amp;quot;Docs-as-code&amp;quot; is the practice of treating documentation the same way software teams treat code: write in plain text (usually Markdown), version-control in Git, review through pull requests, and build automatically with CI/CD.&lt;/p&gt;
&lt;p&gt;The approach has moved from niche to mainstream, and for good reason — it gives technical writers the collaboration and review tools that developers have used for decades. This guide covers the practical setup: which tool to pick, how to structure the review workflow, linting, CI/CD, and the real trade-offs you&amp;#39;ll face.&lt;/p&gt;
&lt;h2 id=&quot;what-docs-as-code-looks-like-in-practice&quot;&gt;What Docs-as-Code Looks Like in Practice&lt;/h2&gt;
&lt;p&gt;In a docs-as-code workflow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Source files are Markdown&lt;/strong&gt; — stored in a Git repository alongside (or separate from) the code they document&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Changes go through pull requests&lt;/strong&gt; — reviewers comment on line-level diffs, just like code review&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A static site generator builds the site&lt;/strong&gt; — MkDocs, Docusaurus, Hugo, or Sphinx turns Markdown into a deployed documentation site&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CI/CD builds and deploys automatically&lt;/strong&gt; — merge to main, and the docs site updates within minutes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is the approach used by major open-source projects: the React docs, Kubernetes docs, and many commercial API documentation sites all use variants of this workflow.&lt;/p&gt;
&lt;h2 id=&quot;choosing-a-static-site-generator&quot;&gt;Choosing a Static Site Generator&lt;/h2&gt;
&lt;p&gt;This is the most consequential decision. The right tool depends on your audience, tech stack, and how much configuration overhead you can absorb.&lt;/p&gt;
&lt;h3 id=&quot;mkdocs--material-theme&quot;&gt;MkDocs + Material theme&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Python projects, internal engineering docs, teams who want to be up in an hour&lt;/p&gt;
&lt;p&gt;MkDocs is the most approachable option. Install it with pip, write a &lt;code&gt;mkdocs.yml&lt;/code&gt;, and run &lt;code&gt;mkdocs serve&lt;/code&gt; for a live preview. The &lt;a href=&quot;https://squidfunk.github.io/mkdocs-material/&quot;&gt;Material for MkDocs&lt;/a&gt; theme adds search, versioning, admonitions, and a professional design out of the box.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# mkdocs.yml&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;site_name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;My&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Docs&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;theme:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;material&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;features:&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;navigation.tabs&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;navigation.sections&lt;/span&gt;
    &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;search.suggest&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;nav:&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;Home:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;index.md&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;Getting Started:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;getting-started.md&lt;/span&gt;
  &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;API Reference:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;api-reference.md&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;MkDocs supports extended Markdown: admonitions (&lt;code&gt;!!! note&lt;/code&gt;), code block line numbers, content tabs, and more. See our &lt;a href=&quot;https://www.markdowntools.com/blog/mkdocs-vs-docusaurus-vs-gitbook&quot;&gt;MkDocs vs Docusaurus vs GitBook comparison&lt;/a&gt; for a full breakdown.&lt;/p&gt;
&lt;h3 id=&quot;docusaurus&quot;&gt;Docusaurus&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; JavaScript/React projects, developer-facing docs, versioned API references&lt;/p&gt;
&lt;p&gt;Docusaurus (by Meta) is the most feature-rich option for developer documentation. It supports versioned documentation, MDX (Markdown with embedded React components), and integrates well with Algolia search. The downside: it requires Node.js and has more configuration overhead.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;npx create-docusaurus@latest my-docs classic
&lt;span class=&quot;hljs-built_in&quot;&gt;cd&lt;/span&gt; my-docs
npm start
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;hugo&quot;&gt;Hugo&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Large sites (thousands of pages), teams who need extreme build speed&lt;/p&gt;
&lt;p&gt;Hugo is the fastest static site generator available — it can build thousands of pages in seconds. The trade-off is a steeper learning curve. Its template system takes time to master, and its Markdown handling has some quirks (it uses Goldmark by default, which differs slightly from CommonMark).&lt;/p&gt;
&lt;h3 id=&quot;sphinx&quot;&gt;Sphinx&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; Python packages, teams that need API auto-documentation from docstrings&lt;/p&gt;
&lt;p&gt;Sphinx uses reStructuredText by default, but the &lt;code&gt;myst-parser&lt;/code&gt; extension gives you Markdown input with full Sphinx features. It&amp;#39;s the standard for Python library documentation (NumPy, Django, Flask) because its autodoc system reads Python docstrings and generates API reference pages automatically.&lt;/p&gt;
&lt;h2 id=&quot;the-review-workflow&quot;&gt;The Review Workflow&lt;/h2&gt;
&lt;p&gt;The core of docs-as-code is Git-based review. Here&amp;#39;s what a typical workflow looks like.&lt;/p&gt;
&lt;h3 id=&quot;branching-strategy&quot;&gt;Branching strategy&lt;/h3&gt;
&lt;p&gt;Keep it simple. Most documentation teams do well with a single &lt;code&gt;main&lt;/code&gt; branch as the source of truth, with short-lived feature branches:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;main                       ← deployed to production
├── docs/update-auth-section
├── docs/add-webhook-guide
└── docs/fix-typos-api-ref
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Some teams mirror software release branches (&lt;code&gt;v2.1&lt;/code&gt;, &lt;code&gt;v2.2&lt;/code&gt;) for versioned documentation.&lt;/p&gt;
&lt;h3 id=&quot;pull-request-review&quot;&gt;Pull request review&lt;/h3&gt;
&lt;p&gt;When a writer opens a PR, reviewers see a line-level diff. They can comment on specific sentences rather than leaving vague feedback on a shared document. This is a significant productivity gain over wiki-based review.&lt;/p&gt;
&lt;p&gt;Tips for effective docs PRs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Keep PRs focused&lt;/strong&gt; — one topic or section per PR, not &amp;quot;update all the docs&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use the PR description&lt;/strong&gt; to explain the &lt;em&gt;why&lt;/em&gt; (&amp;quot;users were confused about X, so I clarified Y&amp;quot;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Request review from subject matter experts&lt;/strong&gt; for accuracy; editors for style&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Require at least one approval before merge&lt;/strong&gt; — documentation errors in production are real bugs&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;reviewing-large-diffs-for-non-git-users&quot;&gt;Reviewing large diffs for non-git users&lt;/h3&gt;
&lt;p&gt;For reviewers who aren&amp;#39;t comfortable with git diffs, our &lt;a href=&quot;https://www.markdowntools.com/tools/markdown-diff&quot;&gt;Markdown Diff tool&lt;/a&gt; lets them paste the before and after versions of a section and see a colour-coded comparison — no git access required.&lt;/p&gt;
&lt;h2 id=&quot;linting-markdownlint-and-vale&quot;&gt;Linting: markdownlint and Vale&lt;/h2&gt;
&lt;p&gt;Linting documentation catches problems before review: broken syntax, inconsistent formatting, and prose style issues.&lt;/p&gt;
&lt;h3 id=&quot;markdownlint&quot;&gt;markdownlint&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/DavidAnson/markdownlint&quot;&gt;markdownlint&lt;/a&gt; enforces Markdown syntax consistency. Run it in CI to catch:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Missing blank lines around headings&lt;/li&gt;
&lt;li&gt;Hard tabs instead of spaces&lt;/li&gt;
&lt;li&gt;Duplicate headings on the same page&lt;/li&gt;
&lt;li&gt;Inconsistent list marker styles (&lt;code&gt;-&lt;/code&gt; vs &lt;code&gt;*&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Lines over a configured maximum length&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;npm install -g markdownlint-cli
markdownlint &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;**/*.md&amp;#x27;&lt;/span&gt; --ignore node_modules
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Configure it with a &lt;code&gt;.markdownlint.json&lt;/code&gt; file in the repository root:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-json&quot;&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;default&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;line-length&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;line_length&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;120&lt;/span&gt; &lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;&amp;quot;no-duplicate-heading&amp;quot;&lt;/span&gt;&lt;span class=&quot;hljs-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;hljs-punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;vale-prose-style-linting&quot;&gt;Vale: prose style linting&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://vale.sh&quot;&gt;Vale&lt;/a&gt; is a linter for prose style. It enforces:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A style guide (Microsoft, Google, or a custom one you define)&lt;/li&gt;
&lt;li&gt;Banned words and phrases (&amp;quot;simply&amp;quot;, &amp;quot;just&amp;quot;, &amp;quot;easy&amp;quot;, &amp;quot;obviously&amp;quot;)&lt;/li&gt;
&lt;li&gt;Consistent terminology (always &amp;quot;API key&amp;quot;, never &amp;quot;api key&amp;quot; or &amp;quot;apikey&amp;quot;)&lt;/li&gt;
&lt;li&gt;Passive voice frequency limits&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-ini&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# .vale.ini&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;StylesPath&lt;/span&gt; = styles
&lt;span class=&quot;hljs-attr&quot;&gt;MinAlertLevel&lt;/span&gt; = suggestion

&lt;span class=&quot;hljs-section&quot;&gt;[*.md]&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;BasedOnStyles&lt;/span&gt; = Vale, Microsoft
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Vale catches what a human style reviewer would catch — at scale, in seconds. It&amp;#39;s what separates mature docs-as-code setups from basic ones.&lt;/p&gt;
&lt;h2 id=&quot;cicd-building-and-deploying-docs&quot;&gt;CI/CD: Building and Deploying Docs&lt;/h2&gt;
&lt;p&gt;The CI pipeline for documentation is usually simpler than for software. A typical GitHub Actions workflow for a MkDocs site:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;# .github/workflows/docs.yml&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Docs&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;CI&lt;/span&gt;

&lt;span class=&quot;hljs-attr&quot;&gt;on:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;push:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;branches:&lt;/span&gt; [&lt;span class=&quot;hljs-string&quot;&gt;main&lt;/span&gt;]
  &lt;span class=&quot;hljs-attr&quot;&gt;pull_request:&lt;/span&gt;

&lt;span class=&quot;hljs-attr&quot;&gt;jobs:&lt;/span&gt;
  &lt;span class=&quot;hljs-attr&quot;&gt;lint:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;runs-on:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;steps:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;with:&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;node-version:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;20&amp;#x27;&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ci&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;npx&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;markdownlint&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;**/*.md&amp;#x27;&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--ignore&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;node_modules&lt;/span&gt;

  &lt;span class=&quot;hljs-attr&quot;&gt;build:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;runs-on:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;needs:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;lint&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;steps:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;actions/setup-python@v5&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;with:&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;python-version:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;3.12&amp;#x27;&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;pip&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;mkdocs-material&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;mkdocs&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;build&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--strict&lt;/span&gt;

  &lt;span class=&quot;hljs-attr&quot;&gt;deploy:&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;if:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;github.ref&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;refs/heads/main&amp;#x27;&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;needs:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;build&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;runs-on:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class=&quot;hljs-attr&quot;&gt;steps:&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;uses:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;actions/setup-python@v5&lt;/span&gt;
        &lt;span class=&quot;hljs-attr&quot;&gt;with:&lt;/span&gt;
          &lt;span class=&quot;hljs-attr&quot;&gt;python-version:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&amp;#x27;3.12&amp;#x27;&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;pip&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;mkdocs-material&lt;/span&gt;
      &lt;span class=&quot;hljs-bullet&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;hljs-attr&quot;&gt;run:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;mkdocs&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;gh-deploy&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;--force&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;--strict&lt;/code&gt; flag on &lt;code&gt;mkdocs build&lt;/code&gt; fails the build if there are any warnings — broken internal links, missing pages — which catches link rot before it ships to production.&lt;/p&gt;
&lt;h2 id=&quot;structuring-markdown-for-documentation&quot;&gt;Structuring Markdown for Documentation&lt;/h2&gt;
&lt;p&gt;Well-structured Markdown makes navigation, search, and updates easier.&lt;/p&gt;
&lt;h3 id=&quot;one-topic-per-file&quot;&gt;One topic per file&lt;/h3&gt;
&lt;p&gt;Each file should cover one complete, focused topic. When a file grows beyond 600–800 lines, it&amp;#39;s usually a sign it should be split into separate pages.&lt;/p&gt;
&lt;h3 id=&quot;consistent-frontmatter&quot;&gt;Consistent frontmatter&lt;/h3&gt;
&lt;p&gt;Define a standard frontmatter schema and enforce it across the team:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-yaml&quot;&gt;&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;title:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Authentication&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;Guide&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;description:&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;How&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;authenticate&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;API&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;requests&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;API&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;keys&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;OAuth&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;2.0&lt;/span&gt;&lt;span class=&quot;hljs-string&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;hljs-attr&quot;&gt;last_updated:&lt;/span&gt; &lt;span class=&quot;hljs-number&quot;&gt;2026-05-15&lt;/span&gt;
&lt;span class=&quot;hljs-meta&quot;&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&quot;heading-discipline&quot;&gt;Heading discipline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;One &lt;code&gt;# H1&lt;/code&gt; per file (the page title)&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;##&lt;/code&gt; and &lt;code&gt;###&lt;/code&gt; for sections and subsections&lt;/li&gt;
&lt;li&gt;Don&amp;#39;t skip heading levels (don&amp;#39;t jump from &lt;code&gt;##&lt;/code&gt; to &lt;code&gt;####&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Write headings that make sense out of context — search engines and screen readers encounter them in isolation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more on heading-based navigation and anchor links, see our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-internal-links&quot;&gt;guide to Markdown internal links&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;callouts-and-admonitions&quot;&gt;Callouts and admonitions&lt;/h3&gt;
&lt;p&gt;Most documentation generators support callout blocks. Use them to highlight warnings, tips, and important notes. In MkDocs Material:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;!!! warning &amp;quot;Breaking change&amp;quot;
&lt;span class=&quot;hljs-code&quot;&gt;    This endpoint was deprecated in v2.0 and will be removed in v3.0.
    Migrate to `/v2/auth` before upgrading.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In Docusaurus (MDX):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;:::warning Breaking change
This endpoint was deprecated in v2.0...
:::
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;See our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-admonitions-callouts-complete-guide&quot;&gt;Markdown admonitions guide&lt;/a&gt; for platform-specific admonition syntax.&lt;/p&gt;
&lt;h3 id=&quot;code-blocks-always-specify-the-language&quot;&gt;Code blocks: always specify the language&lt;/h3&gt;
&lt;p&gt;Always add a language identifier to fenced code blocks. It improves syntax highlighting, helps readers orient quickly, and some generators use the language tag for copy-button behavior.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-markdown&quot;&gt;&lt;span class=&quot;hljs-code&quot;&gt;```bash
curl -X POST https://api.example.com/v1/tokens \
  -H &amp;quot;Authorization: Bearer sk_live_...&amp;quot; \
  -H &amp;quot;Content-Type: application/json&amp;quot;
```&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;For more on code block highlighting across platforms, see our &lt;a href=&quot;https://www.markdowntools.com/blog/markdown-syntax-highlighting-complete-guide&quot;&gt;syntax highlighting guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-trade-offs-docs-as-code-isnt-for-everyone&quot;&gt;The Trade-offs: Docs-as-Code Isn&amp;#39;t for Everyone&lt;/h2&gt;
&lt;p&gt;Docs-as-code has real costs. Be honest with yourself about them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Learning curve for non-developers&lt;/strong&gt; — technical writers who haven&amp;#39;t used Git may need weeks to become comfortable with branching, rebasing, and conflict resolution. Budget for training.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Less intuitive for SME contributions&lt;/strong&gt; — subject matter experts who write occasional documentation may struggle with the Git workflow. Solutions: a web-based editor (Netlify CMS, TinaCMS) for non-technical contributors, or a contribution guide with screenshots.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No rich inline collaboration&lt;/strong&gt; — Google Docs-style simultaneous editing doesn&amp;#39;t exist in Git-based workflows. PRs cover review, but live co-writing requires a workaround.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Image management is awkward&lt;/strong&gt; — images live in the repo or an external CDN. Large images bloat repository size; external images can go dead. Establish an image convention early.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Docs-as-code is clearly the right choice when:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Documentation lives alongside code and should move with it through releases&lt;/li&gt;
&lt;li&gt;The team is primarily developers already working in Git daily&lt;/li&gt;
&lt;li&gt;You need versioned docs to match versioned software releases&lt;/li&gt;
&lt;li&gt;You want to enforce a style guide at scale with Vale and markdownlint&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;It might not be the right choice when:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Most contributors are non-technical&lt;/li&gt;
&lt;li&gt;You need real-time collaborative editing&lt;/li&gt;
&lt;li&gt;The documentation is heavily visual (screenshots, interactive diagrams)&lt;/li&gt;
&lt;li&gt;You don&amp;#39;t have bandwidth to maintain the toolchain&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;getting-started-a-minimal-setup&quot;&gt;Getting Started: a Minimal Setup&lt;/h2&gt;
&lt;p&gt;If you want to try docs-as-code without committing to a full setup:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a GitHub repository for your documentation&lt;/li&gt;
&lt;li&gt;Install MkDocs Material: &lt;code&gt;pip install mkdocs-material&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Create &lt;code&gt;mkdocs.yml&lt;/code&gt; with the minimal config shown above&lt;/li&gt;
&lt;li&gt;Write a &lt;code&gt;docs/index.md&lt;/code&gt; as your home page&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;mkdocs serve&lt;/code&gt; to preview locally&lt;/li&gt;
&lt;li&gt;Enable GitHub Pages and run &lt;code&gt;mkdocs gh-deploy&lt;/code&gt; to publish&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You&amp;#39;ll have a live documentation site in under an hour. From there, add markdownlint to CI, then Vale when you&amp;#39;re ready to enforce style.&lt;/p&gt;
&lt;h2 id=&quot;related-reading&quot;&gt;Related Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/mkdocs-vs-docusaurus-vs-gitbook&quot;&gt;MkDocs vs Docusaurus vs GitBook: which should you use?&lt;/a&gt; — full decision guide with setup examples&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-for-github-complete-workflow-guide&quot;&gt;Markdown for GitHub: the complete workflow guide&lt;/a&gt; — GitHub-specific Markdown features, PR templates, wikis&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-in-confluence-what-works&quot;&gt;Markdown in Confluence: what works and what doesn&amp;#39;t&lt;/a&gt; — for teams who need docs in both systems&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-admonitions-callouts-complete-guide&quot;&gt;Markdown admonitions and callouts guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.markdowntools.com/blog/markdown-syntax-highlighting-complete-guide&quot;&gt;Markdown syntax highlighting guide&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
  </entry>
</feed>
