Markdown Cheatsheet
Flavors and Renderers
Use this Markdown reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
The Lineage
There is no single Markdown. John Gruber's original 2004 Markdown.pl had no formal specification, so every implementation guessed at the edge cases differently. CommonMark exists to end that.
| Flavor | What it is |
|---|---|
| Markdown (original) | Gruber's 2004 syntax page plus Markdown.pl. Ambiguous, effectively frozen |
| CommonMark | A strict, testable spec with a 600-case conformance suite. The baseline |
| GFM | CommonMark plus five extensions: tables, task lists, strikethrough, autolinks, an HTML tag filter |
| MultiMarkdown | Academic focus: footnotes, citations, tables with spanning, metadata, cross references |
| Markdown Extra | PHP-era extension set: tables, definition lists, footnotes, abbreviations, attribute blocks |
| Pandoc Markdown | The largest superset. Everything above plus a document-conversion pipeline |
| MDX | Markdown with JSX. Components in prose, import and export statements, JS expressions |
Write to CommonMark, add GFM's five extensions when the target is a Git forge, and treat everything else as opt-in for a pipeline you control.
Feature Support
| Feature | CommonMark | GFM | GitHub site | Pandoc | Markdown Extra | MDX |
|---|---|---|---|---|---|---|
| Tables | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Task lists | ✗ | ✓ | ✓ | ✓ | ✗ | ✓ |
| Strikethrough | ✗ | ✓ | ✓ | ✓ | ✗ | ✓ |
| Bare-URL autolinks | ✗ | ✓ | ✓ | ✓ | ✗ | ✓ |
| Footnotes | ✗ | ✗ | ✓ | ✓ | ✓ | ✓ |
| Definition lists | ✗ | ✗ | ✗ | ✓ | ✓ | ✗ |
| Abbreviations | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ |
| Alerts and callouts | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ |
| Math | ✗ | ✗ | ✓ | ✓ | ✗ | ✓ |
| Mermaid diagrams | ✗ | ✗ | ✓ | ✗ | ✗ | ✓ |
| Emoji shortcodes | ✗ | ✗ | ✓ | ✓ | ✗ | ✗ |
| Front matter | ✗ | ✗ | Shown as a table | ✓ | ✗ | ✓ |
| Raw HTML | ✓ | Filtered | Sanitized | ✓ | ✓ | JSX instead |
| Components | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ |
The single most portable subset: ATX headings, paragraphs, - bullets, 1. ordered lists, **bold**, *italic*, backtick code spans, fenced code blocks, inline links, images, and blockquotes. That set renders identically everywhere.
MDX Specifics
MDX compiles Markdown into a component tree, so it is a superset syntactically and a subset practically: some Markdown that works elsewhere is a syntax error in MDX.
import Callout from "./Callout"; # Heading <Callout type="warning"> A component, with **Markdown** inside it. </Callout> The answer is {1 + 2}.
What changes:
- A bare
<or{in prose is parsed as JSX and must be escaped or wrapped in a code span. This breaks a lot of copied Markdown. - Raw HTML is JSX, so attributes are camelCase and
classbecomesclassName. - Every tag must be closed, so
<br>must be<br />. - Indented code blocks are unreliable. Always use fences.
- HTML comments do not work. Use
{/* a JSX comment */}.
MDX 2 and 3 are stricter than MDX 1, and the usual migration failure is an unescaped < or a self-closing tag written without the slash.
Renderer Libraries
| Language | Library | Notes |
|---|---|---|
| JavaScript | markdown-it | CommonMark compliant, plugin ecosystem, fast |
| JavaScript | remark and unified | AST based, the toolchain behind MDX and most site generators |
| JavaScript | marked | Small and fast, historically loose on the spec |
| JavaScript | micromark | The CommonMark and GFM core underneath remark |
| Go | goldmark | Hugo's renderer, CommonMark compliant, extensible |
| Rust | pulldown-cmark, comrak | Comrak implements GFM |
| Python | markdown-it-py, mistune, Python-Markdown | The last is the Markdown Extra lineage |
| Ruby | commonmarker, kramdown | Commonmarker wraps cmark-gfm, kramdown powers Jekyll |
| C | cmark, cmark-gfm | The reference implementation, and GitHub's fork |
| Any | pandoc | A converter, not a web renderer |
Sanitizing matters as much as parsing. If Markdown from an untrusted source reaches a browser, run the HTML through a sanitizer such as DOMPurify or rehype-sanitize, or disable raw HTML in the parser. A renderer that passes HTML through is an XSS hole.
Linting
markdownlint is the de facto linter, available as a CLI, a VS Code extension, and a pre-commit hook. Rules are identified as MD###.
npx markdownlint-cli2 "**/*.md" npx markdownlint-cli2 --fix "**/*.md"
{
"default": true,
"MD013": { "line_length": 100, "code_blocks": false },
"MD033": { "allowed_elements": ["details", "summary", "kbd", "img"] },
"MD041": false
}The rules people most often relax:
| Rule | What it checks | Why it gets disabled |
|---|---|---|
MD013 | Line length | Conflicts with one-sentence-per-line |
MD033 | No inline HTML | Needed for details and img sizing |
MD041 | First line is a top-level heading | Front matter supplies the title |
MD024 | No duplicate headings | Changelogs repeat section names |
MD029 | Ordered-list numbering style | Conflicts with writing 1. everywhere |
MD036 | No emphasis as a heading | Pseudo-headings in tight docs |
Formatting
prettier formats Markdown out of the box and is the pragmatic choice, since most repositories already run it.
npx prettier --write "**/*.md" npx prettier --check "**/*.md"
{
"proseWrap": "preserve",
"tabWidth": 2
}What it normalizes: bullet markers to -, emphasis to *, heading style to ATX, table alignment, and list indentation. proseWrap is the setting to decide deliberately.
"preserve"leaves your line breaks alone. The safe default."always"hard wraps toprintWidth, which produces enormous diffs on any edit."never"joins each paragraph onto one line.
Run the formatter and the linter in that order, and let the formatter own anything the linter only complains about cosmetically. Two tools fighting over the same rule is worse than either alone.