Why Markdown Needs A Formal Spec
Markdown's simplicity is also its weakness. The original specification left enough ambiguity that multiple, incompatible implementations emerged. Each "flavor" parsed and rendered Markdown differently, which created real problems for developers and writers who expected consistent output across platforms. CommonMark addresses this by providing a rigorous specification that formalizes Markdown syntax based on how it's actually used in the wild.
The project defines a standardized core syntax along with a comprehensive test suite. Implementations can validate themselves against this spec, ensuring that Markdown rendered on one platform matches another. GitHub's announcement in 2017 captured the intent clearly: CommonMark lets people "continue using Markdown the same way they always have while offering developers a comprehensive specification and reference implementations to interoperate and display Markdown in a consistent way between platforms."
GitHub's own history illustrates the problem. In 2012, the company created GitHub Flavored Markdown (GFM) to fix parsing shortcomings, building it on the Sundown parser. By 2017, GitHub switched GFM to use cmark, the CommonMark reference implementation in C, while keeping GFM's extensions on top of the standardized base. Visual Studio Code similarly targets CommonMark through the markdown-it library. A list of CommonMark implementations now spans C, C#, JavaScript, and other languages.
How CommonMark Parsers Work
CommonMark parsers such as cmark and commonmark.js don't convert Markdown straight to HTML. Instead, they first build an Abstract Syntax Tree (AST) and then render that tree as HTML. This two-step pipeline gives developers a hook between parsing and rendering where they can manipulate or extend the content programmatically.
Core Syntax Philosophy
CommonMark deliberately supports a strict subset of Markdown syntax. This is by design: the goal is to establish the core language that John Gruber originally specified, not to add every feature imaginable. Platforms that adopt CommonMark typically build supersets on top of it. GFM is one example—it supports all CommonMark syntax but extends it with tables and other additions that suit GitHub's needs.
CommonMark's supported syntax includes paragraphs, headings, emphasis, horizontal rules, lists, links, images, blockquotes, and code blocks. The commonmark.js dingus editor is a practical way to experiment with syntax and inspect rendered HTML and AST output.
Paragraphs And Line Breaks
Paragraphs are continuous blocks of text separated by at least one blank line. They render as HTML <p> elements. For an explicit line break within a paragraph, end a line with two spaces or a backslash (\).
| Syntax | Rendered HTML |
|---|---|
| This is a line of text | <p>This is a line of text</p> |
| This is a line of text And another line of text And another but the same paragraph | <p>This is a line of text And another line of text And another but the same paragraph</p> |
| This is a paragraph And another paragraph And another | <p>This is a paragraph</p> <p>And another paragraph</p> <p>And another</p> |
| Two spaces after a line of text Or a post-fixed backslash\ Both means a line break | <p>Two spaces after a line of text<br /><br>Or a post-fixed backslash<br /><br>Both means a line break</p> |
Headings: Two Syntaxes
CommonMark supports both ATX and Setext heading styles. ATX headings use hash symbols and cover all six levels (h1 through h6). A space must separate the hashes from the text, and the number of hashes determines the level. Trailing hashes are permitted but have no effect, as in # Heading 1 #.
| Syntax | Rendered HTML |
|---|---|
| # Heading 1 | <h1>Heading 1</h1> |
| ## Heading 2 | <h2>Heading 2</h2> |
| ### Heading 3 | <h3>Heading 3</h3> |
| #### Heading 4 | <h4>Heading 4</h4> |
| ##### Heading 5 | <h5>Heading 5</h5> |
| ###### Heading 6 | <h6>Heading 6</h6> |
| ## Heading 2 ## | <h2>Heading 2</h2> |
Setext headings are limited to levels 1 and 2. An h1 uses equals signs underneath the text, while h2 uses dashes. At least one marker character is required.
| Syntax | Rendered HTML |
|---|---|
| Heading 1 = | <h1>Heading 1</h1> |
| Heading 2 - | <h2>Heading 2</h2> |
Emphasis And Strong Emphasis
Single asterisks (*) or underscores (_) around text produce emphasis, rendered as HTML <em>. Double symbols produce strong emphasis, rendered as <strong>. The opening and closing markers must match, and no space may appear between a marker and the enclosed text.
| Syntax | Rendered HTML |
|---|---|
_Italic_ | <em>Italic</em> |
*Italic* | <em>Italic</em> |
__Bold__ | <strong>Bold</strong> |
**Bold** | <strong>Bold</strong> |
Horizontal Rules
Three or more asterisks, hyphens, or underscores on their own line create a horizontal rule. Spaces between markers are allowed but not required.
| Syntax | Rendered HTML |
|---|---|
*** | <hr /> |
* * * | <hr /> |
--- | <hr /> |
- - - | <hr /> |
___ | <hr /> |
_ _ _ | <hr /> |
Ordered And Unordered Lists
List syntax supports both bullet and ordered lists. Unordered lists accept asterisks, pluses, or hyphens as markers. Ordered lists use numbers followed by a period or closing parenthesis. Consistency is required within a list: once you begin with one marker style, you must continue with it for all items in that list.
| Syntax | Rendered HTML |
|---|---|
| * one * two * three | <ul> <li>one</li> <li>two</li> <li>three</li> </ul> |
| + one + two + three | <ul> <li>one</li> <li>two</li> <li>three</li> </ul> |
| - one - two - three | <ul> <li>one</li> <li>two</li> <li>three</li> </ul> |
| - one - two + three | <ul> <li>one</li> <li>two</li> </ul> <ul> <li>three</li> </ul> |
| 1. one 2. two 3. three | <ol> <li>one</li> <li>two</li> <li>three</li> </ol> |
| 3. three 4. four 5. five | <ol start="3"> <li>three</li> <li>four</li> <li>five</li> </ol> |
| 1. one 2. two 3. three | <ol> <li>one</li> <li>two</li> <li>three</li> </ol> |
Link Syntax
CommonMark supports both inline and reference formats for links, following these rules:
- Links render as the HTML Anchor element.
- The inline format uses
[value](URL "optional-title"), with no space between the brackets. - The reference format uses
[value][id]for the reference, and[id]: href "optional-title"for the hyperlink definition, separated by at least a line. - The
idis the Definition Identifier and may contain letters, numbers, spaces, and punctuation. - Definition Identifiers are case-insensitive.
- Automatic Links are also supported, where the URL is enclosed in less than (
<) and greater than (>) symbols and displayed literally.
<!--Markdown-->
[Google](https://google.com “Google”)
<!--Rendered HTML-->
<a href="https://google.com" title="Google">Google</a>
<!--Markdown-->
[Google](https://google.com)
<!--Rendered HTML-->
<a href="https://google.com">Google</a>
<!--Markdown-->
[Comparing Styling Methods in Next.js](/2020/09/comparing-styling-methods-next-js)
<!--Rendered HTML-->
<a href="https://www.smashingmagazine.com/2020/09/comparing-styling-methods-next-js">Comparing Styling Methods In Next.js</a>
<!--Markdown-->
[Google][id]
<!--At least a line must be in-between-->
<!--Rendered HTML-->
<a href="https://google.com" title="Google">Google</a>
<!--Markdown-->
<https://google.com>
<!--Rendered HTML-->
<a href="https://google.com">google.com</a>
<!--Markdown-->
<[email protected]>
<!--Rendered HTML-->
<a href="mailto:[email protected]">[email protected]</a>
Images
Images in CommonMark similarly follow the inline and reference formats used for links:
- Images render as the HTML image element.
- The inline format uses
. - The reference format uses
![alt text][id]for the reference, and[id]: image-url "optional-title"for the definition, separated by at least a blank line. - The image title is optional, and the image URL may be relative.
<!--Markdown-->

<!--Rendered HTML-->
<img src="image-url" alt="alt text" title="optional-title" />
<!--Markdown-->
![alt text][id]
<!--At least a line must be in-between-->
<!--Markdown-->
<!--Rendered HTML-->
<img src="image-url" alt="alt text" title="optional-title" />
Blockquotes
To create the HTML Block Quotation element, prefix each line with the greater than symbol (>).
<!--Markdown-->
> This is a blockquote element
> You can start every new line
> with the greater than symbol.
> That gives you greater control
> over what will be rendered.
<!--Rendered HTML-->
<blockquote>
<p>This is a blockquote element
You can start every new line
with the greater than symbol.
That gives you greater control
over what will be rendered.</p>
</blockquote>
Blockquotes can be nested:
<!--Markdown-->
> Blockquote with a paragraph
>> And another paragraph
>>> And another
<!--Rendered HTML-->
<blockquote>
<p>Blockquote with a paragraph</p>
<blockquote>
<p>And another paragraph</p>
<blockquote>
<p>And another</p>
</blockquote>
</blockquote>
</blockquote>
They can also contain other Markdown elements, such as headers, code, and list items:
<!--Markdown-->
> Blockquote with a paragraph
> # Heading 1
> Heading 2
> -
> 1. One
> 2. Two
<!--Rendered HTML-->
<blockquote>
<p>Blockquote with a paragraph</p>
<h1>Heading 1</h1>
<h2>Heading 2</h2>
<ol>
<li>One</li>
<li>Two</li>
</ol>
</blockquote>
Inline Code
The HTML Inline Code element is created by delimiting text with back-ticks (`). Use double back-ticks when the enclosed text must contain a literal back-tick.
<!--Markdown-->
`inline code snippet`
<!--Rendered HTML-->
<code>inline code snippet</code>
<!--Markdown-->
`<button type='button'>Click Me</button>`
<!--Rendered HTML-->
<code><button type='button'>Click Me</button></code>
<!--Markdown-->
`` There's an inline back-tick (`). ``
<!--Rendered HTML-->
<code>There's an inline back-tick (`).</code>
Code Blocks
For the HTML Preformatted Text element, CommonMark offers two approaches: a fenced code block using at least three matching back-ticks (`) or tildes (~), or an indented code block starting with at least four spaces on a new line.
<!--Markdown-->
```
const dedupe = (array) => [...new Set(array)];
```
<!--Rendered HTML-->
<pre><code>const dedupe = (array) => [...new Set(array)];</code></pre>
<!--Markdown-->
const dedupe = (array) => [...new Set(array)];
<!--Rendered HTML-->
<pre><code>const dedupe = (array) => [...new Set(array)];</code></pre>
Inline HTML
John Gruber's original spec note on inline HTML states that any markup that is not covered by Markdown's syntax, you simply use HTML itself. The only restrictions are that block-level HTML elements — e.g. <div>, <table>, <pre>, <p> — must be separated from surrounding content by blank lines, and their start and end tags should not be indented with tabs or spaces.
In practice, most writing happens with a Markdown flavor that already extends CommonMark to cover syntax not currently supported by the core spec.
Current Status
CommonMark remains an active project, with the specification last updated on April 6, 2019. While it gears up for a 1.0 release with remaining issues to resolve, many popular applications already support the spec. Notably, GitHub Flavored Markdown bases its formal specification on CommonMark, underscoring the effort toward standardization. This drive does not prevent the creation of flavors that extend supported syntax.
Resources
- Introducing Markdown by John Gruber
- CommonMark official website
- GitHub Flavored Markdown Spec
- cmark official repo
- GitHub's fork of cmark
- Markdown on Wikipedia
- Markdown Guide




