CommonMark vs GFM: Markdown Flavors and Why They Render Differently
“Markdown” sounds like one thing, but in practice it is a family of incompatible dialects. The same file can render differently in a GitHub README, a static-site generator, and a chat app. Knowing which flavor you are targeting — and which features are portable — saves hours of confused debugging.
The CommonMark Baseline
John Gruber's original 2004 Markdown specification was intentionally informal. It solved readability but left parser behavior ambiguous. By 2014, every major platform shipped a slightly different implementation. CommonMark (version 0.30, 2022) finally gave the format a rigorous reference spec with a deterministic parsing algorithm.
The CommonMark baseline covers the 90% everyone agrees on: ATX headings (#), paragraphs, emphasis (*em*, **strong**), inline and fenced code, ordered and unordered lists, blockquotes, horizontal rules, links, and images. If your Markdown uses only these constructs, it renders identically across all modern parsers.
GitHub-Flavored Markdown (GFM)
GFM extends CommonMark with features that became de facto standards for code collaboration:
- Tables — pipe-delimited cells with a separator row. Widely supported but entirely absent from CommonMark.
- Task lists —
- [ ]and- [x]render as checkboxes. GitHub, GitLab, and most static-site generators support them; plain CommonMark treats them as literal text. - Strikethrough —
~~text~~. - Autolinked URLs — bare URLs like
https://example.combecome clickable without the<>wrapping CommonMark requires. - Disallowed raw HTML — GFM filters tags like
<script>and<iframe>even when the underlying parser allows raw HTML passthrough.
Other Flavors You Will Meet
MultiMarkdown adds footnotes, citations, and definition lists — common in academic and long-form writing. Pandoc's Markdown is the superset of supersets, with math, fenced divs, and attribute syntax. MDX embeds JSX inside Markdown, which Next.js documentation sites and Storybook rely on heavily. Discord and Slack use Markdown-like formatters that ignore link syntax entirely and have their own emphasis rules.
Security: Escape Before Rendering
Markdown authors can embed raw HTML. If you are rendering user-submitted Markdown (comments, forum posts, issue trackers), the parser output is HTML that your frontend will inject into the DOM — exactly where XSS attacks live. A 2018 Ghost CMS advisory (CVE-2018-16492) and multiple Jupyter Notebook issues both stemmed from allowing raw HTML in trusted-author contexts that were later opened to the public.
Two canonical defenses: run the rendered HTML through a sanitizer like DOMPurify before injecting, or configure your parser to escape HTML entirely (markdown-it exposes this via the html: false option; marked exposes sanitize: true but deprecated it in favor of post-render sanitization). Never trust the input.
Common Pitfalls
- Hard line breaks — a single newline inside a paragraph is treated as a space, not a line break. Use two trailing spaces, or a backslash at the end of the line, to force
<br>. - Emphasis inside words —
un*friendly*lydoes not emphasize “friendly” in CommonMark because the*markers are surrounded by word characters. Use_around the whole word instead, or add spaces. - Indented code blocks — four leading spaces creates a code block. This surprises users who indent list continuations and watch them turn into unstyled preformatted text.
- Setext vs ATX headings —
=====under a line of text makes an<h1>, but only if the line above is not empty and not part of a list. ATX (#) is more portable.
Picking a Parser in 2026
markdown-it is the dominant JavaScript parser: extensible via plugins, CommonMark-compliant by default, adds GFM via markdown-it-gfm-alerts and similar. marked is smaller and faster but less spec-compliant. For server-side, remark + rehype (the unified.js ecosystem) produce an AST you can transform before emitting HTML — ideal for MDX and static-site generators.
For a one-off conversion where you paste text and want HTML back, a minimal in-browser converter covers 90% of real cases without the dependency weight of a full parser.
Key Takeaways
- CommonMark is the rigorous baseline; GFM extends it with the features you probably think of as “Markdown”
- Portable docs should stick to CommonMark — tables and task lists are not universal
- Always sanitize rendered HTML when the Markdown source is user-controlled
- Hard line breaks and emphasis inside words trip up even experienced writers
Want to paste some Markdown and see the HTML it generates? Try our Markdown to HTML converter — runs entirely in your browser, no upload.
Frequently Asked Questions
- Is CommonMark the same as GitHub's Markdown?
- No. CommonMark is the standardized baseline (2014-2022 spec work); GitHub-Flavored Markdown (GFM) is CommonMark plus tables, task lists, strikethrough, and autolinked URLs. Files that use only CommonMark features render identically everywhere; files that use GFM-only features render as literal text in strict CommonMark parsers.
- Why does a single newline not create a line break in Markdown?
- By design — single newlines are treated as spaces so authors can wrap paragraphs at a natural column width without fragmenting them. To force a line break, end the line with two spaces or a backslash. Most Markdown confusion around formatting comes from this rule.
- Should I let users submit raw Markdown on my site?
- Only if you sanitize the rendered HTML (DOMPurify is the canonical choice) or disable raw HTML passthrough in the parser. Several CMS platforms shipped XSS vulnerabilities because they trusted Markdown input from users who could embed script tags directly.