Why flavors exist

John Gruber created Markdown in 2004, with feedback from Aaron Swartz. It was two things at once: a syntax, described on a web page in friendly prose, and a Perl script, Markdown.pl, that converted it to HTML. The last version of that script, 1.0.1, dates from 17 December 2004; it has not been updated since.

Prose is not a specification. Gruber's description did not say what should happen in many edge cases: how far a nested list item must be indented, whether a list may start right after a paragraph line, what * inside a word means. Markdown.pl itself had bugs, so "do what the script does" was no answer either. And Markdown has no syntax errors: every input produces some output. Each new tool (for PHP, Python, Ruby, JavaScript) therefore made its own decisions, and many also added features that Gruber's version lacked, such as tables and footnotes.

A Markdown flavor (or dialect) is one of these variants: a set of parsing rules plus a set of extras, defined by a specification or simply by what one renderer does. When someone says "this is valid Markdown", the honest follow-up question is: in which flavor, rendered by which tool?

CommonMark: the common core

CommonMark is a precise, unambiguous specification of Markdown's core syntax, written by John MacFarlane (who also wrote Pandoc), together with a suite of several hundred examples that doubles as a test suite. Every rule comes with input and expected HTML, so two implementations can prove that they agree.

  • Current version: 0.31.2, dated 28 January 2024. As of 2026 it is still the latest; there is no 1.0. The spec is licensed CC BY-SA 4.0.
  • What it covers: blocks (paragraphs, headings, thematic breaks, code blocks, block quotes, lists, HTML blocks, link reference definitions) and inlines (emphasis, links, images, code spans, autolinks, raw HTML, entities, line breaks).
  • What it leaves out: tables, task lists, strikethrough and footnotes. Those are extensions, defined elsewhere.
  • Reference implementations: cmark in C and commonmark.js in JavaScript. The online "dingus" at spec.commonmark.org lets you paste Markdown and see the HTML and the syntax tree.
  • Who builds on it: the CommonMark site lists Discourse, GitHub, GitLab, Reddit, Qt, Stack Overflow and Swift. VS Code's Markdown preview targets CommonMark too.

What CommonMark decides

Most of the spec settles questions that the original description left open. Three examples you will meet in practice; all examples on this page are rendered by the site's own CommonMark renderer (see below).

A heading needs a space after the #. Without it, the line is a paragraph:

#Outage update

# Outage update
<p>#Outage update</p>
<h1>Outage update</h1>

A numbered list may interrupt a paragraph only if it starts at 1. Otherwise a sentence that happens to wrap at a number would turn into a list:

The meter count went up by
14. That is normal for March.

The steps are:
1. Read the meter
2. Log the value
<p>The meter count went up by
14. That is normal for March.</p>
<p>The steps are:</p>
<ol>
<li>Read the meter</li>
<li>Log the value</li>
</ol>

An underscore inside a word never starts emphasis, so names such as sensor_id_value are safe. An asterisk inside a word does:

The sensor_id_value field is empty on meter*2*b.
<p>The sensor_id_value field is empty on meter<em>2</em>b.</p>

Older renderers disagree on all three: some accept #Heading, some turn 14. into a list, some emphasize part of snake_case_names. The details are on the pages about headings and line breaks, lists and emphasis.

GitHub Flavored Markdown

GitHub Flavored Markdown (GFM) is the flavor GitHub uses in .md files, issues, pull requests, comments, Discussions and wikis (see Markdown on GitHub for the tour). Its specification is a copy of the CommonMark spec with extra sections marked "(extension)". In its own words, GFM is "a strict superset of CommonMark": every CommonMark document renders the same way in GFM, apart from what the extensions add.

The published GFM spec is version 0.29-gfm, from April 2019. It has not been updated to CommonMark 0.30 or 0.31, so in the fine print it trails the CommonMark spec by two revisions; for everyday writing the difference does not matter.

Extensions

A Markdown extension is syntax that a flavor or renderer adds on top of the core, such as tables or footnotes. Extensions are where flavors differ most: the same characters can be a table in one tool and plain text with pipes in another. The GFM spec defines exactly five:

Extension Syntax Result
Tables Rows with |, a delimiter row of --- An HTML table with a header row (Tables)
Task list items - [ ] and - [x] Checkboxes (Lists and task lists)
Strikethrough ~text~ or ~~text~~ Struck-through text
Extended autolinks www.example.com, https://…, email addresses Links without <…> around them
Disallowed raw HTML <script>, <iframe>, <style> and six more tags The tags are escaped (HTML in Markdown and safety)

The renderer on this site supports two of these, tables and strikethrough, so they can be shown live:

| Region   | Status   |
|----------|----------|
| Deventer | Restored |
| Zwolle   | ~~Down~~ Restored |
<table>
<thead>
<tr>
<th>Region</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>Deventer</td>
<td>Restored</td>
</tr>
<tr>
<td>Zwolle</td>
<td><s>Down</s> Restored</td>
</tr>
</tbody>
</table>

Even a shared extension can differ in detail. GFM accepts one or two tildes for strikethrough; markdown-it, the renderer used here, only accepts two:

~~Two tildes~~ and ~one tilde~
<p><s>Two tildes</s> and ~one tilde~</p>

Task lists and extended autolinks are not supported by this site's renderer. On GitHub, this:

- [x] Change approved
- [ ] Customers notified

Status page: www.kestrel-energy.example/status
☑ Change approved
☐ Customers notified

Status page: www.kestrel-energy.example/status   (shown as a link)

In a plain CommonMark renderer the same text shows the literal [x] and [ ], and the web address stays plain text.

Beyond the GFM spec: GitHub's extras

Much of what people think of as "GitHub Markdown" is not in the GFM spec at all. GitHub added these features to its own pipeline later, and they are documented in GitHub's help pages rather than in a specification:

  • Footnotes [^1] (2021)
  • Mermaid diagrams, and maps and 3D models, in fenced code blocks (2022)
  • Math with $…$ and $$…$$ (2022)
  • Alerts such as > [!NOTE] and > [!WARNING] (December 2023)
  • Emoji shortcodes such as :tada:, @mentions and #123 references

Some of these work only in some places on GitHub (footnotes, for example, are not supported in wikis), and other platforms copy some of them with small differences. They are covered on Markdown on GitHub and GitLab.

Other flavors you will meet

GFM and CommonMark cover most day-to-day writing, but documentation tools, converters and note apps bring their own dialects:

Flavor or tool Where you meet it Worth knowing
GitLab Flavored Markdown GitLab issues, merge requests, wikis, files CommonMark plus the GFM extensions plus GitLab's own: !123 merge request references, [[_TOC_]], multiline quotes with >>>, PlantUML diagrams
kramdown Jekyll, and therefore GitHub Pages GitHub Pages' default Markdown processor (as of 2026 GitHub Pages runs Jekyll 3.10 with kramdown 2.4). Not CommonMark: a page can render differently in the repository view and on the Pages site
Python-Markdown MkDocs documentation sites Follows the original Markdown.pl behavior closely, not CommonMark; features are added with extensions in mkdocs.yml
Pandoc Markdown Pandoc conversions to Word, PDF, slides A large extended dialect; Pandoc can also read and write strict CommonMark and GFM
PHP Markdown Extra, MultiMarkdown Older CMS and writing tools Early extended dialects, both registered as variants in RFC 7764
Obsidian Flavored Markdown The Obsidian notes app CommonMark, GFM and LaTeX math plus [[wikilinks]], ==highlights== and callouts
MDX Docusaurus and other React-based sites (.mdx files) Markdown plus JSX components, so characters such as < and { can have a special meaning

Chat tools are a separate case: Slack's mrkdwn uses *single asterisks* for bold and has no headings, and Microsoft Teams supports a subset. They look like Markdown but follow their own rules; see Markdown in chat, notes and AI tools.

Family of Markdown flavors. The original Markdown of 2004 leads to two branches: CommonMark, specified in version 0.31.2 of 2024, and dialects with their own rules such as Pandoc Markdown, kramdown, Python-Markdown, PHP Markdown Extra and MultiMarkdown. CommonMark is the base of GitHub Flavored Markdown (spec 0.29-gfm, 2019), GitLab Flavored Markdown and markdown-it with tables and strikethrough. GitHub.com adds alerts, footnotes, Mermaid and math on top of GFM. Original Markdown Markdown.pl, 2004 CommonMark spec 0.31.2, 2024 Dialects with their own rules Pandoc, kramdown, Python-Markdown, PHP Markdown Extra, MultiMarkdown GFM spec 0.29-gfm, 2019 GitLab Flavored CommonMark + GFM + more markdown-it + tables, strikethrough GitHub.com + alerts, footnotes, Mermaid, math
Most current tools build on CommonMark and add extensions; older dialects kept their own parsing rules.

Pandoc Markdown

Pandoc Markdown is the extended Markdown dialect of Pandoc, the document converter by John MacFarlane (the CommonMark spec's author). It is registered as its own variant in RFC 7764 and is built for long documents: reports, books and papers that end up as Word files, PDFs or slides. Pandoc treats its extensions as switches that can be turned on or off one by one, and it can also read strict commonmark or gfm instead. That makes it a practical tool for checking how a document behaves in another flavor; see Converting Markdown.

The renderer on this site: markdown-it

Every live example in this topic is rendered by markdown-it 15.0.2 (September 2026), a JavaScript library that follows the CommonMark spec and, in its default settings, adds GFM-style tables and strikethrough. It does not support task lists, footnotes, alerts, front matter, Mermaid or math without plugins. VS Code's built-in Markdown preview uses the same library, which is why the preview there is CommonMark-based and not identical to GitHub.

markdown-it also has a few options, all off by default. When an example on these pages uses one, the text says so. With linkify, bare URLs become links, but only when they have a scheme such as https://; since version 15, an address that starts with www. is no longer linked by default, unlike GFM:

Check https://status.example.com or www.example.com for updates.
<p>Check <a href="https://status.example.com">https://status.example.com</a> or www.example.com for updates.</p>

With typographer, straight quotes become curly quotes, -- becomes a dash, ... an ellipsis and (c) a copyright sign. Pleasant in prose, and a trap when someone copies a command or a value from the rendered text:

"Planned" maintenance -- Saturday... (c) Kestrel Energy
<p>“Planned” maintenance – Saturday… © Kestrel Energy</p>

The point is not markdown-it itself, but the habit: when Markdown renders oddly, find out which renderer and which options are in play before you blame your syntax.

Writing Markdown that works everywhere

A README is read on GitHub, in an editor preview, in a package registry and sometimes on a documentation site. A runbook may be pasted into a ticket or a wiki. A few habits keep the result predictable:

  • Stay within the CommonMark core for structure: # headings with a space, - bullets, fenced code blocks, inline links. Use an extension only where the target supports it.
  • Use blank lines generously: before and after headings, lists, code blocks, tables and HTML. Many differences between flavors disappear when blocks are clearly separated.
  • Prefer a backslash for a hard line break in CommonMark-based renderers (GitHub, GitLab, VS Code) over two trailing spaces, which are invisible and often removed by editors. Python-Markdown and kramdown do not support the backslash form, so where the target is unknown, avoid hard breaks: use a list or a new paragraph.
  • Indent nested lists consistently with spaces, aligned with the text of the parent item. Mixed tabs and spaces are the most common cause of lists that render differently in two tools.
  • Check in the real target: GitHub's Preview tab, the documentation site's local preview, or the CommonMark dingus for the strict core. A linter catches many portability problems before anyone sees them.