Markdown to HTML with live explainer and GFM↔CommonMark diff

Convert Markdown to HTML and see what each token actually produces - with a live spec-rule explainer and a visible GFM vs CommonMark switch.

There's no shortage of Markdown-to-HTML converters - paste in, copy out, done. What's missing when you're learning or debugging is the why: why this exact HTML came out, and where GFM and CommonMark actually diverge. You get both here - the spec rule behind every element when you point at it, and a flavor switch sitting in the open at the top of the page instead of buried under a settings menu.

Flavor
How GitHub renders it: tables, strikethrough, bare-URL autolinks, task lists, emoji shortcodes.
Markdown source
Hover a button - the markdown syntax lands here.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
99 words · ≈ 1 min · 610 chars Syntax clean
HTML preview
Hover an element on the right - the spec rule lands here.

Markdown to HTML

Hover an element on the right - the HTML tag lights up, with the spec rule next to it.

What works

  • bold and italic
  • strikethrough (GFM only)
  • inline code and code blocks
const greeting = "Hello, world";
console.log(greeting);

Markdown is a spec, not a single tool. CommonMark and GFM render some things differently.

Feature CommonMark GFM
Tables
Strikethrough
Autolinks

More at https://spec.commonmark.org - autolink test.

42 elements in output
Copy HTML

What renders differently

7 elements in your markdown wouldn't render in CommonMark - they'd stay markdown text.

Click a tile - the affected lines light up in the editor.

Runs entirely in your browser. Your markdown isn't uploaded, stored, or analysed.

How do I convert Markdown to HTML?

Type or paste markdown into the left pane, and the HTML appears on the right - no run button, no wait. Point at an element in the preview and the live explainer shows which markdown token produced which HTML tag, with the matching spec rule. Three copy buttons underneath give you semantic HTML, inline-styled HTML for email, or a bare fragment.

In practice, four steps:

  1. Type or paste markdown into the left pane.
  2. Pick a flavor - GFM (default) or CommonMark.
  3. Point at any element in the preview to read the spec rule.
  4. Click Semantic, Inline-styled, or Fragment depending on where the HTML is going.

Everything runs in your browser. Your markdown doesn't leave the page.

What's the difference between GFM and CommonMark?

CommonMark is the strict baseline spec; GFM (GitHub Flavored Markdown) is CommonMark plus a handful of extensions GitHub added for its own rendering. Every GFM document is also valid CommonMark, but plain CommonMark can't render a few GFM-specific constructs at all. In this converter, five of those differences are visible.

ConstructCommonMarkGFMNote
TablesGFM spec extension
Strikethrough (~~text~~)GFM spec extension
Bare URLs as linksLinkify - <https://…> autolinks work in both
Task listsGFM spec extension
Emoji shortcodesPlugin (markdown-it-emoji), not part of GFM spec

Click the toggle at the top and the output flips. Underneath the converter, the "What renders differently" section only lists the lines that actually differ in your current source - not a theoretical inventory, the concrete delta for the markdown you're working with right now.

Why does _filename_ produce <em>?

Markdown treats underscores the same way as asterisks - as emphasis markers. _italic_ and *italic* both render as <em>italic</em>. When your filename starts AND ends with an underscore, the parser reads those underscores as brackets around an emphasis span, and the filename comes out italic. A classic gotcha for paths, variable names, and file extensions.

The nuance is where the underscores sit. CommonMark distinguishes between intra-word and edge underscores: file_name_v2 stays as text because the underscores are between letters or digits. _filename_ has underscores on the outer edges - that's where emphasis kicks in.

Three ways out:

  • Backslash escape: \_filename\_ renders as _filename_, the underscores stay visible.
  • Inline code: `_filename_` renders in monospace, so the markdown isn't parsed at all.
  • Hyphens instead of underscores where possible - -filename- sidesteps the rule entirely.

Point at _filename_ in the preview and the exact spec rule shows up in the explainer strip just above.

Clean HTML for a CMS, email client, or embedded content

The default output is clean, semantic HTML - the right shape for most targets: CMS, static site, blog engine. Two cases need something else. Email clients ignore external CSS and want style attributes inlined on every element. Embedded snippets shouldn't bring their own <html> or <body> tags that would collide with the surrounding layout.

Three copy buttons under the converter handle exactly that - instead of one button that doesn't tell you what it copies:

  1. Semantic. Clean tags, no inline styles. The default for CMS editors like WordPress, Ghost, or Notion, plus Hugo, Eleventy, MDX.
  2. Inline-styled. Style attributes on every <h1>, <p>, <pre>, <table> - survives Gmail, Outlook, and Apple Mail. The stylesheet stays conservative: no custom fonts, no CSS variables.
  3. Fragment. No <html>, <body>, <head> - just the content, ready to drop into an existing wrapper.

Rule of thumb: CMS and static-site generators take Semantic, HTML newsletters take Inline-styled, embedded snippets take Fragment.

Frequently Asked Questions

Which markdown constructs are supported?

All CommonMark block and inline constructs except raw HTML - embedded <script> or <div> tags are escaped, not passed through (safe by default). Plus the GFM extensions: tables, strikethrough (~~gone~~), linkify for bare URLs, task lists (- [ ] and - [x]), and emoji shortcodes (:sparkles:, a markdown-it-emoji plugin, not part of the GFM spec). Fenced code blocks get syntax highlighting via highlight.js for JS/TS, HTML/XML, CSS, JSON, Bash, Python, Go, Rust, YAML, and Markdown. Unknown language hints render as plain code blocks.

Is my markdown stored anywhere?

No. The conversion runs entirely in your browser - your markdown never leaves the page. When you click Share, the source is base64-encoded into the link (the full URL is capped at 2000 characters), but only on your click.

Why are my tables suddenly plain text?

The flavor toggle is probably set to CommonMark. Tables are part of the GFM extension - in plain CommonMark the pipes stay as text. Switch to GFM and the same source renders as a table.

Can I convert HTML back to Markdown?

Not yet. HTML to Markdown is getting its own tool - the input shapes are too different to share a page cleanly. When the tool ships, the link will land here.

Which spec version does the converter use?

markdown-it@14. CommonMark compliance is against the 0.30 spec - the current spec version is 0.31.2, but markdown-it renders the constructs added between 0.30 and 0.31.2 identically. The GFM bits cover the extensions in the GitHub Flavored Markdown Spec (0.29-gfm): tables, strikethrough, linkify for bare URLs, and task lists.