Skip to content
Control Plane Labs

Markdown ↔ HTML Preview

Side-by-side Markdown editor with GitHub-flavored Markdown support, live preview, and a sanitized HTML pane you can copy.

Privacy: Everything runs in your browser: marked parses the Markdown and DOMPurify sanitizes the HTML locally. Your document is never uploaded or logged.

Deploy runbook

Rollback is one command. Read the whole thing before you start.

Checklist

  • Drain the node pool
  • Verify kubectl get pods -w is quiet
  • Flip the feature flag
Step Owner Timeout
Drain oncall 5m
Verify oncall 2m

Autoscaling stays off until verify passes.

kubectl rollout undo deploy/api

Old two-command rollback is gone. Status: https://example.com/status

87 words in, 878 bytes of HTML out. GitHub-flavored Markdown is on: tables, task lists, strikethrough, and bare-URL autolinks. Output is sanitized with DOMPurify, so <script>, inline event handlers, and javascript: URLs are stripped from both panes. Conversion happens in your browser; the input is mirrored into the URL up to 2 KB so the link is shareable.

What it does

Paste Markdown on the left, get the rendered result on the right, and flip the right pane to HTML source when you want the markup rather than the picture. GitHub Flavored Markdown extensions are on, so tables, task lists, strikethrough, and bare-URL autolinks all behave the way they do in a repo README. The HTML pane is the exact string the preview renders, so whatever you copy is what you saw.

One direction only: Markdown to HTML. There is no HTML-to-Markdown mode, and that is deliberate. The reverse conversion is lossy for anything Markdown cannot express, and it tends to produce Markdown files with HTML soup inside them.

How it works

Parsing is done by marked with gfm: true, which implements CommonMark plus the extensions in the GitHub Flavored Markdown spec. The resulting HTML string is then passed through DOMPurify before anything touches the DOM. Both steps run in the page. No network request is made, and the document is never uploaded.

Sanitization matters here because GFM deliberately allows inline HTML. A Markdown document is untrusted input the moment it comes from somewhere other than your own keyboard — a CMS field, a webhook payload, a pull request body — and rendering it with innerHTML is the textbook way to turn a comment box into a cross-site scripting hole. DOMPurify parses the HTML and drops anything not on an allowlist: <script>, <iframe>, on* event handler attributes, and javascript: URLs are gone. Ordinary structural tags survive, including <details>, <kbd>, and <sup>.

The Markdown source is mirrored into the ?q= query parameter, up to 2 KB, so a short document survives a reload and can be shared as a link. Longer input stays in the textarea only.

Why the preview does not match GitHub exactly

This is the question that generates the most confusion, so it is worth being precise. Two separate things cause a mismatch.

The first is hard breaks. Under CommonMark, a single newline inside a paragraph is a soft break and renders as a space, so two consecutive lines become one paragraph. GitHub renders comment fields — issues, PRs, review threads — with hard breaks enabled, because people type in them like a chat box. Files in a repository render with the standard soft-break rule. The “single newline = <br>” toggle switches between the two. If your text looks right in an issue comment and wrong in a README, this is why.

The second is that GitHub’s pipeline does not stop at Markdown. After the Markdown-to-HTML step it post-processes the output: heading anchor links, emoji shortcodes like :shipit:, @ mentions, #123 issue references, and commit SHA autolinks. None of that is in the GFM spec — it is application behavior layered on top, and no standalone renderer reproduces it. Task list checkboxes are a related case: the spec’s output is <input type="checkbox" disabled>, and GitHub makes them clickable by writing the toggled state back into the source file. Without an owner for the file, there is nothing to write back to, so the checkboxes here render read-only, per spec.

When to reach for it

Checking a README or a CONTRIBUTING.md before you push, when opening a browser tab beats waiting on a preview build. Sanity-checking Markdown that arrived from an API before you paste it somewhere it will be rendered. Grabbing clean semantic HTML for an email template or a CMS field that wants markup rather than Markdown. Confirming that a table you hand-wrote actually has the right number of pipes — misaligned delimiter rows silently fall back to a paragraph, which is obvious in the preview and invisible in the source.

It is also a fast way to see what a sanitizer keeps. Paste suspicious HTML into the Markdown pane, look at the HTML source pane, and you have your answer.

Frequently asked questions

Which Markdown flavor does this render?+
CommonMark plus the GitHub Flavored Markdown extensions, via marked with gfm: true. That gets you tables, task lists, strikethrough, and bare-URL autolinks. It is not byte-identical to GitHub's renderer — GitHub post-processes HTML (heading anchors, emoji shortcodes, @ mentions, issue references) after the Markdown step, and none of that is part of the spec.
Why does my raw HTML disappear from the output?+
GFM allows inline HTML, but the output here is passed through DOMPurify before it is shown or copied. <script>, <iframe>, on* event handler attributes, and javascript: URLs are removed. Safe tags such as <details>, <kbd>, and <sup> survive. If you need unsanitized HTML, do that in your own build pipeline where you control the trust boundary.
My line breaks are collapsing into one paragraph. Why?+
That is correct CommonMark behavior: a single newline is soft, so consecutive lines join into one paragraph. GitHub renders comment fields with hard breaks enabled, which is why the same text looks different there. Tick the "single newline = <br>" box to match that behavior; leave it off to match how README.md renders in a repo.
Can it convert HTML back into Markdown?+
No. This tool is Markdown to HTML only. HTML-to-Markdown needs a separate DOM-walking converter (Turndown and friends), and the round trip is lossy for anything Markdown cannot express — nested tables, colspans, inline styles, arbitrary attributes. Converting those back produces HTML embedded in your Markdown, which usually is not what you wanted.
Are the task list checkboxes interactive?+
No. GFM emits <input type="checkbox" disabled> for task list items, and that disabled attribute is part of the spec's output. GitHub's own UI adds interactivity by writing the toggled state back to the source document, which only makes sense when something owns the file.