CodeApr 9, 2026 · Updated Oct 14, 2026·7 min

Markdown Mastery: Write, Convert & Preview

Markdown is a family of dialects. We ran the 652 CommonMark spec examples and 18 everyday snippets through seven parsers, GitHub's included, to show which syntax works everywhere and what changes between a README, a GitHub comment and a Python script.

📝
Try the Markdown Preview
Write Markdown with live formatted preview
→
DG
Derek Giordano
Designer & Developer
In this guide
01Markdown Is a Family of Dialects02Eighteen Snippets, Six Renderers03Line Breaks Depend on Where You Type04Lists and Indentation05Tables, Task Lists, Strikethrough and Footnotes Are Extensions06Raw HTML and Safety07Why READMEs, Notebooks and Prompts Use Markdown08Converting Between Markdown and HTML

Markdown Is a Family of Dialects

John Gruber released Markdown 1.0.1 in December 2004 as two things at once: a plain-text syntax and a Perl script that turned it into HTML. The syntax spread faster than any single definition of it, so "Markdown" now names a family of dialects. CommonMark (spec version 0.31.2, with 652 numbered examples) pins down the core rules. GitHub Flavored Markdown (GFM) adds tables, task lists, strikethrough, bare-URL links and an HTML tag filter on top. Older parsers still follow Gruber's looser rules.

The differences show up the moment a file moves between tools. We ran all 652 CommonMark examples through seven parsers, one of them in two configurations, and compared each output with the HTML the spec expects, after normalizing whitespace:

ParserCommonMark 0.31.2 examples matchedWhy it differs
commonmark.js 0.31.2 (reference implementation)652 / 652
markdown-it 14.1.0, CommonMark preset652 / 652
markdown-it-py 4.0.0, CommonMark preset652 / 652
cmark-gfm, GitHub's parser (cmarkgfm 2024.11.20)624 / 652GFM extensions change 28 outputs, mostly emphasis, HTML blocks and autolinks
markdown-it 14.1.0, default preset580 / 652Raw HTML is off by default, so HTML blocks are escaped
marked 4.3.0, as our Markdown Preview and Markdown to HTML call it574 / 652GFM on, line breaks off; links, entities and list edge cases
mistune 3.2.0, default466 / 652Escapes HTML; its own list and emphasis rules
Python-Markdown 3.10.2346 / 652 (362 with Extra + sane_lists)Follows Gruber's original rules, not CommonMark

Eighteen Snippets, Six Renderers

Scores hide which constructs break. These snippets show where the dialects split. The GitHub columns come from cmark-gfm, the C library GitHub publishes. GitHub's rendering API was rate-limited from our test machine, so we ran the same library locally: once with footnotes on, as GitHub Docs describe them, and once with hard line breaks, the way GitHub treats comments.

InputCommonMarkGitHub .md fileGitHub commentOur tools (marked 4.3.0)Python-Markdown 3.10.2mistune 3.2.0
#Heading (no space)paragraphparagraphparagraphparagraphheadingparagraph
Nested list, 2-space indentnestednestednestednestedflatnested
Nested list, 4-space indentnestednestednestednestednestednested
List right after a text linelistlistlistlistparagraphlist
The year was then 2. placeparagraphparagraphparagraphparagraphparagraphparagraph
Pipe tabletexttabletabletabletexttext
- [x] tasktextcheckboxcheckboxcheckboxtexttext
~~old~~textstruckstruckstrucktexttext
Bare https:// URLtextlinklinklinktexttext
Single line breakone lineone lineline breakone lineone lineone line
Two trailing spacesbreakbreakbreakbreakbreakbreak
Backslash at line endbreakbreakbreakbreakbackslash shownbreak
```js fenceclass="language-js"<pre lang="js"><pre lang="js">class="language-js"not a code blockclass="language-js"
[^1] footnotetextfootnotefootnotetexttexttext
<details> with Markdown insidekept, inside parsedkept, inside parsedkept, inside parsedkept, inside parsedkept, inside not parsedescaped
snake_case_nameno emphasisno emphasisno emphasisno emphasisno emphasisno emphasis
some*thing*hereemphasisemphasisemphasisemphasisemphasisemphasis
Setext heading (Title over =====)headingheadingheadingheadingheadingheading

markdown-it's default preset (not shown) matched CommonMark plus tables and strikethrough, but left task lists, bare URLs and footnotes as text and escaped raw HTML unless you pass html: true. The 2. place row is a CommonMark rule, not a bug: an ordered list can interrupt a paragraph only when it starts at 1, so a line that happens to begin with "2." stays part of the sentence in every parser we tested.

Line Breaks Depend on Where You Type

GitHub Docs spell out the split: in issues, pull requests and discussions, GitHub renders a single newline as a line break, while in an .md file the same two lines join into one. In files you need two trailing spaces, a backslash at the end of the line, or a <br> tag. Our cmark-gfm runs agreed, and turning on comment-style hard breaks cost the parser 66 more CommonMark examples (558 instead of 624).

Two trailing spaces worked in every parser we tested, but they are invisible in an editor, and many editors strip trailing whitespace on save. The backslash is visible and part of CommonMark; only Python-Markdown printed it as a literal backslash. Our Markdown Preview and Markdown to HTML tools call marked with line breaks off, so they render a README the way GitHub does. Switching marked to comment-style breaks dropped its CommonMark score from 574 to 514.

Lists and Indentation

In CommonMark, a nested item belongs to its parent when it is indented to the column where the parent's text starts: two spaces under - , three under 1. . Python-Markdown predates that rule and wants four spaces, so our two-space nested list came out flat there. It also refuses to start a list on the line right after a paragraph, which CommonMark, GitHub, marked and mistune all accept.

The safe habit that every parser in the table agreed on: indent nested items by four spaces and leave a blank line before a list.

Shopping:

- Fruit
    - Apple
    - Pear
- Vegetables

Tables, Task Lists, Strikethrough and Footnotes Are Extensions

None of these four is in CommonMark. The GFM spec (version 0.29-gfm) defines tables, task list items, strikethrough and extended autolinks; GitHub Docs document footnotes separately. Python-Markdown's Extra bundle adds tables, footnotes and fenced code, plus abbreviations, attribute lists, definition lists and Markdown inside HTML, but not task lists or strikethrough, which we confirmed by running the same snippets with Extra on.

A GFM table needs a header row and a delimiter row of hyphens; colons in the delimiter row set alignment, and the pipes at the start and end of each line are optional:

| Tool    | Format |  Size |
|:--------|:------:|------:|
| Preview | HTML   | 12 KB |

The Markdown Table Generator writes the delimiter row and alignment colons for you.

Raw HTML and Safety

CommonMark passes raw HTML through untouched, which is how <details> blocks and <br> tags work inside Markdown. GFM adds a tag filter: it replaces the opening < of nine tags (title, textarea, style, xmp, iframe, noembed, noframes, script and plaintext) with &lt; so they show as text. markdown-it's default preset and mistune escape all HTML instead.

marked does neither: HTML in the input reaches the output. That is why our Markdown Preview runs the rendered HTML through DOMPurify before it shows or copies anything. If your own site renders Markdown that other people write, sanitize the HTML after rendering; escaping the Markdown first breaks code spans and links.

Why READMEs, Notebooks and Prompts Use Markdown

Benjamin Lee's "Ten simple rules for documenting scientific software" (PLoS Computational Biology, 2018, retrieved through PubMed) treats the README as a project's homepage on GitHub, Bitbucket and GitLab and argues it should be readable as raw source, which is why Markdown, reStructuredText or plain text beat HTML there. Lee's rule of thumb: assume the README is the only documentation people read, so it should cover installation, where the full docs live, the license, how to run the tests and a quickstart.

In data analysis, Markdown carries the prose of literate-programming documents. Baumer and Udwin (WIREs Computational Statistics, 2015) describe R Markdown re-running every code chunk each time the document renders, so the report follows the data instead of numbers pasted in by hand, a step they list among the sources of trivial errors. Orozco and colleagues (Journal of Economic Surveys, 2020) recommend the same pattern for empirical economics: R Markdown with knitr and pandoc, or Stata's dyndoc commands since Stata 15. Both papers came from a Wiley Scholar Gateway search.

Markdown is also the default structure for language-model prompts, and the evidence there is mixed. He and colleagues (Microsoft and MIT, 2024, found through alphaXiv) formatted the same prompts as plain text, Markdown, YAML and JSON for four GPT models on six benchmarks. On MMLU, GPT-3.5-turbo scored 59.7% with JSON and 50.0% with Markdown, while GPT-4-1106-preview scored 81.2% with Markdown and 73.9% with JSON. Only 16% of GPT-3.5 answers were identical between the Markdown and JSON versions, and no format won everywhere. Markdown headings in a prompt are a format choice to test against your own task, not a guaranteed gain.

Converting Between Markdown and HTML

Our Markdown to HTML tool uses marked with the same settings as the preview, so its output matches GitHub's file rendering for the constructs above, with two exceptions: fenced code gets a language- class instead of GitHub's lang attribute, and footnotes stay as text. The HTML to Markdown tool parses the input with the browser's own HTML parser and writes tables, nested lists and code-fence languages back out; anything Markdown has no syntax for, such as classes, inline styles or merged table cells, is dropped.

Habits that held up in every parser we tested:

DG
Derek Giordano
Written by the creator of Ultimate Design Tools. BA in Business Marketing.
⚡ Try the free Markdown Badge Generator →
⚡ Try the free Changelog Generator →