Markdown Flavors: CommonMark, GitHub Flavored Markdown and Others
Why the same Markdown can look different in two places: the original description, the CommonMark specification, GitHub Flavored Markdown and the other dialects you will meet.
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:
cmarkin C andcommonmark.jsin 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:,@mentionsand#123references
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.
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.