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.

FlavorWhat it is
Markdown (original)Gruber's 2004 syntax page plus Markdown.pl. Ambiguous, effectively frozen
CommonMarkA strict, testable spec with a 600-case conformance suite. The baseline
GFMCommonMark plus five extensions: tables, task lists, strikethrough, autolinks, an HTML tag filter
MultiMarkdownAcademic focus: footnotes, citations, tables with spanning, metadata, cross references
Markdown ExtraPHP-era extension set: tables, definition lists, footnotes, abbreviations, attribute blocks
Pandoc MarkdownThe largest superset. Everything above plus a document-conversion pipeline
MDXMarkdown 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

FeatureCommonMarkGFMGitHub sitePandocMarkdown ExtraMDX
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✓FilteredSanitized✓✓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 class becomes className.
  • 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

LanguageLibraryNotes
JavaScriptmarkdown-itCommonMark compliant, plugin ecosystem, fast
JavaScriptremark and unifiedAST based, the toolchain behind MDX and most site generators
JavaScriptmarkedSmall and fast, historically loose on the spec
JavaScriptmicromarkThe CommonMark and GFM core underneath remark
GogoldmarkHugo's renderer, CommonMark compliant, extensible
Rustpulldown-cmark, comrakComrak implements GFM
Pythonmarkdown-it-py, mistune, Python-MarkdownThe last is the Markdown Extra lineage
Rubycommonmarker, kramdownCommonmarker wraps cmark-gfm, kramdown powers Jekyll
Ccmark, cmark-gfmThe reference implementation, and GitHub's fork
AnypandocA 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:

RuleWhat it checksWhy it gets disabled
MD013Line lengthConflicts with one-sentence-per-line
MD033No inline HTMLNeeded for details and img sizing
MD041First line is a top-level headingFront matter supplies the title
MD024No duplicate headingsChangelogs repeat section names
MD029Ordered-list numbering styleConflicts with writing 1. everywhere
MD036No emphasis as a headingPseudo-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 to printWidth, 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.