Markdown Previewer
DevRender Markdown to HTML with a self-contained, sanitizing renderer — no raw HTML executes, all output is CSP-safe.
On this page
What is a Markdown renderer?#
Markdown is the plain-text shorthand developers use to write rich text: **bold**, # headings, - lists, `code`. A renderer is the program that turns those shorthand marks into the HTML a browser actually displays. README files, documentation sites, issue-tracker comments, chat apps, and static-site generators all run Markdown through a renderer before showing it to you.
This page is a Markdown renderer with one specific design priority: it is safe by construction, not by filtering. Most renderers first turn Markdown into HTML and then try to scrub dangerous tags out of the result — a model where one missed regex means an XSS vulnerability. This tool works the other way around: every character of the source is HTML-escaped before any formatting is applied, and the only tags it ever emits come from a fixed, hardcoded allow-list (p, h1–h6, ul, ol, li, blockquote, pre, code, hr, a, img, strong, em, del, br). There is no code path that can emit a <script> tag from your input, because raw < in the source is escaped to < before the formatter ever sees it. That is what makes it suitable for a strict-CSP static site like this one.
It implements a focused subset of CommonMark plus GitHub-Flavored Markdown essentials: fenced code blocks, ATX headings, paragraphs, ordered and unordered lists, blockquotes, thematic breaks (---), inline code, bold, italic, strikethrough, images, links, and angle autolinks. The same input gives you two outputs side by side — the rendered preview and the raw HTML — so you can see exactly what the renderer produced.
How to use it#
- Type Markdown into the left pane, or click Sample to load a built-in document that exercises every supported feature.
- Switch the output with the two buttons above the right pane:
- Rendered shows the formatted result as it would appear on a web page.
- View HTML shows the exact HTML the renderer emitted, which is what you would paste into a template or hand to a sanitizer audit.
- Click Copy HTML to grab the generated HTML for use elsewhere — pasting into a CMS, embedding in a template, or running through a review.
- Click Clear to empty both panes.
- The status line reports the rendered byte length or notes when the input is empty.
Key features#
- Safe by construction. All source text is HTML-escaped before formatting, and the renderer emits only a fixed allow-list of tags. There is no “then strip the dangerous parts” step to get wrong.
- Dangerous URL schemes dropped. Link and image URLs are scheme-checked;
javascript:,vbscript:,data:,file:, andabout:are blocked, and the offending text is shown literally instead of becoming a live link. - Two views, one source. Toggle between the rendered preview and the raw HTML without re-typing, so you can verify both the visual result and the markup it produced.
- GFM essentials included. Strikethrough (
~~text~~), fenced code blocks with language hints, and task-friendly lists behave the way GitHub-flavored Markdown does. - Zero dependencies, 100% client-side. The renderer is self-implemented — no bundled parsing library — and runs entirely in your browser. Nothing is uploaded, and there is no backend to inject anything.
Worked example#
Click Sample and the left pane fills with a document that exercises the core syntax:
# Heading
A paragraph with **bold**, *italic*, ~~strike~~ and `inline code`.
- bullet one
- bullet two
1. first
2. second
> A blockquote.
[a link](https://example.com)
\`\`\`
const x = 42;
console.log(x);
\`\`\`
Switch to View HTML and you see exactly what the renderer emitted. The paragraph becomes:
<p>A paragraph with <strong>bold</strong>, <em>italic</em>,
<del>strike</del> and <code>inline code</code>.</p>
The link becomes a hard target with safe rel/attributes:
<a href="https://example.com" rel="noopener noreferrer" target="_blank">a link</a>
and the fenced block becomes an escaped <pre><code> that preserves the source verbatim — const x = 42; shows up as text, not as executed script. That last point is the whole security story in miniature: even if you pasted <script>alert(1)</script> into the source, it would render as the literal text <script>alert(1)</script>, because the < is escaped to < before the formatter ever decides what is a tag.
FAQ#
Why would I use this instead of just writing HTML directly?#
Because Markdown is faster to type, easier to read in source form, and lets non-HTML-savvy contributors write content. This renderer is for when you want that convenience without trusting a generic parsing library to also be safe — the safety here comes from the architecture, not from a sanitizer bolted on at the end.
I pasted a <script> tag and it showed up as text. Is that a bug?#
No — that is the intended behaviour and the core safety guarantee. Every character of your input is HTML-escaped before formatting, so any tag-like text in the source is rendered as inert visible text. There is no way through this renderer to get a live <script> into the output.
Why was my javascript: link shown as plain text?#
The renderer scheme-checks every link and image URL. Schemes like javascript:, vbscript:, data:, file:, and about: are blocked because they are the classic vectors for injection attacks. When a URL is blocked, the renderer leaves the original Markdown source as literal text rather than emitting a dangerous anchor.
Does it support tables, task lists, or footnotes?#
No — this is a deliberate, focused subset of CommonMark plus GFM essentials (fenced code, strikethrough, autolinks). Tables and footnotes need a richer parser and a bigger attack surface to police; they are intentionally out of scope here. Stick to the supported constructs listed in the features above.