Markdown Editor & Preview

Write Markdown, see it rendered, and copy the HTML.

74 words

Preview
HTML
Table of contents
  • Markdown Editor & Preview #markdown-editor-preview
  • What works #what-works
  • A table #a-table
  • A task list #a-task-list

Runs in your browser. Nothing you enter is sent anywhere.

How to use it

Write or paste into the Markdown box. The Preview next to it redraws as you type, and the line at the top counts the words in the rendered text. Everything runs in your browser; nothing is uploaded. On a narrow screen, Write and Preview switch between the two.

  1. Leave GitHub tables, task lists and strikethrough ticked for GitHub Flavored Markdown. Untick it for plain CommonMark, where tables, - [ ] task items, ~~strikethrough~~ and bare URLs stay as text.
  2. Copy the result with Copy HTML. The HTML box shows exactly what the preview draws, after sanitizing.
  3. Use Table of contents to see every heading, indented by level, with the #anchor it gets.

The preview sits in a sandboxed frame, so links in it don’t open: a stray click can’t take you away from your draft.

Examples

A README section with a task list and a right-aligned column:

## Setup

- [x] Install Node 22
- [ ] Run `pnpm dev`

| Flag | Default |
| --- | ---: |
| `--port` | 4321 |
<h2 id="setup">Setup</h2>
<ul>
<li><input checked disabled type="checkbox"> Install Node 22</li>
<li><input disabled type="checkbox"> Run <code>pnpm dev</code></li>
</ul>
<table>
<thead>
<tr>
<th>Flag</th>
<th align="right">Default</th>
</tr>
</thead>
<tbody><tr>
<td><code>--port</code></td>
<td align="right">4321</td>
</tr>
</tbody></table>

The count reads 11 words: 22, 4321 and the code spans count too. With the box unticked, the same table comes out as one paragraph of pipes.

Raw HTML and an unsafe link:

Press <kbd>Ctrl</kbd>+<kbd>C</kbd>. <img src="cat.png" width="200" onerror="alert(1)">

[Docs](javascript:alert(1)) and <a href="https://example.com" style="color:red">a link</a>
<p>Press Ctrl+C. <img src="cat.png"></p>
<p><a>Docs</a> and <a href="https://example.com">a link</a></p>

<kbd> isn’t on the allow-list, so the tag goes and its text stays. width, onerror and style are dropped, and the javascript: link keeps its text but loses its href.

Repeated and punctuated headings:

## API
## Errors
### 404: Not found
## Errors
## What's new in v2.0?

The anchors are #api, #errors, #404-not-found, #errors-1 and #whats-new-in-v20.

Good to know

  • The parser is marked, following CommonMark and the GFM spec. GitHub extras outside that spec stay as text: footnotes ([^1]), alerts (> [!NOTE]), emoji shortcodes (:tada:) and math ($x^2$). A mermaid block shows as code with class="language-mermaid", not as a diagram.
  • A single line break is a space. That’s the CommonMark rule, and GitHub follows it in .md files, though not in issue comments. End a line with two spaces or a backslash to get a <br>.
  • The HTML is rebuilt from an allow-list. Only tags Markdown itself produces survive: paragraphs, headings, emphasis, lists, links, images, code, quotes, tables, <hr>, <br>, <del> and task-list checkboxes. Any other tag (<kbd>, <sub>, <details>, <div align="center">) is removed but its text stays. <script>, <style>, <iframe>, <svg>, forms and media go with their content, and HTML comments are removed.
  • Attributes are allow-listed too. Links keep href and title, images src, alt and title, headings their id, code its language-… class, ordered lists start, and table cells align. class, style, width and every on… handler are dropped.
  • Links and images must be http, https, mailto or relative. Anything else, such as javascript: or data:, loses its href or src. Character references are decoded before the check, as a browser would decode them, so javascript&#58alert(1) and a tab hidden in the scheme are caught too.
  • Anchors are lowercase, without punctuation, with a hyphen for each run of spaces. Letters of any script keep their accents and vowel signs (## नमस्ते gets #नमस्ते), and a heading with nothing left, such as one emoji, gets #section. GitHub writes one hyphen per space, so ## C++ & C# is #c--c there and #c-c here.
  • Entities stay as written in the HTML. &copy; and &nbsp; reach the copied HTML unchanged, while the contents list and the word count read them as the characters they stand for.
  • Input is limited to 200,000 characters.

Questions

How do I convert Markdown to HTML?

Paste the Markdown into the box and press Copy HTML. In code, the same library is marked.parse(text), and in a shell pandoc -f gfm -t html README.md does it. Neither sanitizes by default, so run their output through a sanitizer such as DOMPurify before you put someone else’s Markdown on a page.

Why is my HTML tag missing from the preview?

Because it isn’t on the allow-list: only the tags Markdown produces are kept, and the text inside any other tag stays without it. That’s why <details> blocks show their contents flat and <p align="center"> isn’t centered.

Write a link to the heading’s anchor, such as [Errors](#errors), using the anchor listed under Table of contents. A second heading with the same text gets -1, so [More errors](#errors-1) points to the second ## Errors.

Why doesn’t my line break show up?

Markdown joins lines that sit next to each other into one paragraph. Leave a blank line to start a new paragraph, or end the line with two spaces or a backslash for a line break inside it.