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 -wis 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.
Related tools
- JSON formatter for the API response the Markdown came wrapped in.
- YAML ↔ JSON converter for the frontmatter block above the Markdown body.
- Base64 encoder/decoder when the document arrives as an encoded blob from a Git provider API.
- URL encoder/decoder for the links inside your document.
- Regex tester for the search-and-replace pass you are about to run over a directory of Markdown files.
Frequently asked questions
Which Markdown flavor does this render?+
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?+
<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?+
README.md renders in a repo.Can it convert HTML back into Markdown?+
Are the task list checkboxes interactive?+
<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.Related tools
JSON Formatter and Validator
Paste JSON, get it pretty-printed or minified. Validates syntax, highlights errors, works entirely in your browser.
YAML ↔ JSON Converter
Convert YAML to JSON and back in your browser. Resolves anchors and merge keys, handles multi-document files, and lets you pick the output indent.
Base64 Encoder/Decoder
Encode and decode Base64 in your browser, with correct UTF-8 handling, a URL-safe alphabet toggle, and file mode for binary payloads.