Markdown Specification: CommonMark and GFM Explained
September 11, 2026 · 9 min read
Markdown Specification: CommonMark and GFM Explained
The markdown specification most tools follow is CommonMark, a formal spec (version 0.31.2, January 2024) with 652 numbered examples that settle how ambiguous input renders. GitHub Flavored Markdown adds tables, task lists, and strikethrough on top. This guide maps the three layers, shows the rules that matter, and explains how to read the spec.
Why Does Markdown Need a Specification?
John Gruber published Markdown in 2004 as a Perl script and a syntax page. If you're new to the format, start with what Markdown is and come back; this post assumes you've written a few files. That page describes the syntax in prose, with examples, but it doesn't say what should happen in the edge cases. The CommonMark spec's introduction states the problem directly: Gruber's canonical description "does not specify the syntax unambiguously".
The spec then lists the questions the original leaves open. How much indentation does a sublist need? Is a blank line required before a heading or a blockquote? When do list items get wrapped in paragraph tags? Every Markdown implementation in the decade after 2004 answered those questions differently, which is why the same file rendered one way on a blog and another way on GitHub.
CommonMark, first released in 2014, answers each question with a rule and a test case. A parser is "CommonMark compliant" when it passes all of the examples in the spec. That's the whole point of a Markdown spec: not new syntax, but agreed behaviour for the syntax that already existed.
The Three Layers: Gruber, CommonMark, and GFM
It helps to think of the Markdown specification landscape as three layers, each building on the one below.
| Layer | What it is | Status |
|---|---|---|
| Original Markdown (2004) | Gruber's syntax description and Markdown.pl | Informal; no test suite |
| CommonMark (2014, now 0.31.2) | Formal spec with 652 examples and a reference parser | The de facto standard |
| GitHub Flavored Markdown (0.29-gfm) | CommonMark plus five extensions | What GitHub, and many tools, render |
The Markdown GFM spec, published at github.github.com/gfm, describes itself as "a strict superset of CommonMark". Anything valid in CommonMark renders the same way on GitHub. The extensions, each marked "(extension)" in the GFM document, are tables, task list items, strikethrough, extended autolinks, and a disallowed raw HTML filter that strips tags such as <script> and <title>.
Two things follow from that. First, if your file uses only CommonMark syntax, it renders identically everywhere CommonMark is implemented. Second, if it uses a table or - [ ] checkbox, you're relying on an extension, and a plain CommonMark parser prints the pipes and brackets as text. The GitHub Markdown cheat sheet covers the extension syntax in practice; this post stays on the rules underneath.
What Are the Style Guidelines for Markdown Under CommonMark?
The Markdown specification doesn't dictate style. It dictates parsing. But three rules in the spec effectively become style guidelines, because following them is the only way to get predictable output.
Indent list content to the marker, not four spaces. The spec says the position of the text after the list marker determines how much indentation later blocks in that item need. For - item, that's two columns; for 10. item, it's four. Example 255 in the spec shows that - one followed by a blank line and two indented one space yields a list and then a separate paragraph, because one space isn't enough.
- one
two
That renders as a one-item list followed by a paragraph reading "two". Indent two by two spaces and it becomes part of the list item.
Underscores inside words don't create emphasis; asterisks do. Example 360 renders foo_bar_ as literal text, while example 355 renders foo*bar* as foo<em>bar</em>. The rule exists so that snake_case_names survive without escaping. If you want emphasis inside a word, use asterisks.
Changing the ordered-list delimiter starts a new list. Example 302 shows 1. foo, 2. bar, 3) baz producing two lists, the second starting at 3. Mixing . and ) is a rare mistake, but it's one the original syntax never addressed.
Ambiguous Inputs, Rendered Live
The fastest way to understand the Markdown specification is to type its examples into a compliant renderer and watch what happens. The editor below is pre-filled with three inputs the original syntax left ambiguous. Each one now has a single correct answer. To test the list-indentation rule from the previous section, type the two-space example in yourself.
Watch the last block: 3) third starts a second ordered list numbered from 3. And the lazy continuation line joins the blockquote even without a > because example 247 in the spec says a paragraph continuation line inside a quote doesn't need the marker.
Try the same inputs in the CommonMark dingus, the official live tester, and the output matches. That agreement between two independent renderers is what a spec buys you.
How to Read the CommonMark Spec
The spec is long, but it's organised so you can look things up rather than read it end to end.
- Find the section. Sections follow the block-then-inline structure of a document: leaf blocks (headings, code blocks, thematic breaks), container blocks (block quotes, lists), then inlines (emphasis, links, code spans).
- Read the rule, then the examples. Every rule is followed by numbered examples showing input on the left and expected HTML on the right. The example number is stable within a version, which is why bug reports cite "example 360" rather than quoting the rule.
- Check the version string. The header shows the version and date. Parsers state which version they implement; goldmark's README, for instance, says it's compliant with CommonMark 0.31.2. A parser tested against 0.29 may disagree on a handful of examples that changed since.
- Run the tests. The spec ships a
spec.jsonwith every example and aspec_tests.pyscript. If you maintain a parser or a Markdown-processing pipeline, running those 652 cases is the definitive compliance check.
One acknowledged limitation: the spec covers HTML output only. It says nothing about how a PDF exporter should page content or how a terminal viewer should colour a heading, so "CommonMark compliant" tells you about parsing, not presentation.
Which Markdown Spec Does Each Parser Follow?
When a document renders differently in two places, the first question is which Markdown spec each place implements. This table covers the parsers behind the tools people ask about most.
| Parser | Used by | Spec followed |
|---|---|---|
| cmark | Reference implementation (C) | The current CommonMark spec |
| cmark-gfm | GitHub (a fork of cmark) | GFM 0.29-gfm |
| markdown-it | Many JavaScript apps and static site tools | CommonMark, with optional GFM-style plugins |
| goldmark | Hugo and other Go projects | CommonMark 0.31.2, with a GFM extension bundle |
| remark (micromark) | MDX, Docusaurus, this site | CommonMark, GFM via remark-gfm |
| Python-Markdown | MkDocs | Original Markdown syntax, not CommonMark |
The last row is the one that bites. Python-Markdown, which MkDocs uses by default, follows Gruber's original rules and requires four-space indentation for nested lists. A file that nests lists with two spaces looks right on GitHub and flat in MkDocs. That single difference accounts for a large share of "my list won't nest" questions.
Inline HTML is another divergence point. CommonMark's HTML block rules (examples 188 and 189) draw a clear line. A <div> with blank lines around its content lets the inner Markdown render; a <div> with no blank lines keeps the content literal. Platforms then apply their own sanitiser on top. The Markdown vs HTML comparison covers when to reach for raw HTML at all.
What Is a Spec Markdown File?
This question appears in search results because of a naming collision. "Spec Markdown" (spec-md) is a separate tool for writing technical specifications, such as the GraphQL spec, in a Markdown-like syntax with numbered sections and cross-references. It is not the Markdown spec.
A file that follows the Markdown specification, on the other hand, is any ordinary .md file that sticks to CommonMark rules. There's no special extension or header, and every construct in the Markdown cheat sheet has a numbered example behind it. If you want to check a file against the spec, paste it into the dingus or into the editor and compare the rendered output with what you expect.
Markdown Spec FAQ
The markdown specification you're most likely relying on is CommonMark 0.31.2, with GFM's five extensions layered on top when you're on GitHub. Learn the three rules above, check a parser's stated spec version when output differs, and use the numbered examples to settle arguments. To see how any input renders under those rules, paste it into the editor.