Markdown Preview
Edit Markdown on the left and preview the rendered result on the right in real time. Supports headings, bold and italic, inline code, fenced code blocks, lists, links, blockquotes, horizontal rules and tables.
0 words
What is Markdown?
Markdown is not an app, but a set of plain-text rules for expressing document structure. The same .md source renders consistently in any renderer that follows CommonMark 0.31.2 (the base specification, published 2024-01-28) or GitHub Flavored Markdown / GFM 0.29-gfm (published 2019-04-06, a strict superset of CommonMark). The moment a platform adds private post-processing (emoji shortcodes, @mentions, tag filtering), the same source can look different. This previewer works entirely in your browser using an escape-then-render subset, so what you type and what you get are predictable — which is exactly why it is useful for confirming output before you publish.
CommonMark 0.31.2 vs GFM specification differences
The table below settles, in one place, which syntax this tool definitely supports and which belongs to an extension — so you avoid surprises when moving documents between platforms. Sources: CommonMark 0.31.2 (spec.commonmark.org) and GFM 0.29-gfm (github.github.com/gfm).
| Feature | CommonMark 0.31.2 | GFM 0.29-gfm | This previewer |
|---|---|---|---|
| Pipe tables | No | Yes | Yes |
Task lists - [ ] | No | Yes | No |
Strikethrough ~~text~~ | No | Yes | No |
| Bare URL autolink | Angle-bracket <url> only | Bare URLs auto-linked | No bare URLs (use [text](url)) |
Footnotes [^1] | Not in spec | Not in spec (GitHub non-spec extension) | No |
| Intraword underscore emphasis | Disallowed | Same as CommonMark | Simplified rule (see traps table) |
Syntax element reference (with nesting rules)
| Element | Syntax | Renders as | Nesting / boundary rules |
|---|---|---|---|
| Headings | # to ###### + space | <h1>–<h6> | 1–6 levels; space required after #; optional closing # |
| Emphasis | **b**/__b__ bold, *i*/_i_* italic | <strong>/<em> | Paired delimiters; not inside code spans |
| Inline code | `code` | <code> | Backtick content kept literal, no emphasis parsed |
| Code block | ``` + optional language | <pre><code> | Fenced pair; content not parsed as Markdown |
| Blockquote | > text | <blockquote> | Consecutive > lines merge; blank line ends |
| Lists | -/*/+ or 1. | <ul>/<ol> | Continuation indented to content column; ordered starts at 1 |
| Links | [text](url) | <a> | URL limited to http/https/mailto/tel/#/absolute path |
| Images |  | Not supported here | Current subset skips image syntax |
| Horizontal rule | ---/***/___ | <hr> | 3+ of the same character |
| Tables | | cols + |---| delimiter | <table> | Needs delimiter row, else degrades to text |
How to Use This Tool
- Type or paste Markdown content into the editor
- The preview updates in real time after a 150ms debounce
- Click "Load Sample" to try all supported syntax, or "Clear" to start over
- A word counter below the editor shows the combined CJK and English word count
Markdown parsing traps
Note: Most "it rendered differently than I expected" cases below come from misreading the CommonMark rules, not from a tool bug. Preview locally first, then publish to the platform.
| Trap | Common mistake | Correct form | This previewer |
|---|---|---|---|
| Hard line break | Two trailing spaces expect <br> | Use a blank line for a new paragraph | Intra-paragraph newlines collapse to a space, no <br> |
| Emphasis boundary | a_b_c expected emphasized | Use * or spaces | Simplified regex may over-emphasize _; code spans always safe |
| HTML + MD mix | Write MD inside <div> | Exit the HTML block with a blank line | Always escaped first, raw HTML not rendered |
| List continuation indent | Continuation at column 0 | Indent to the content column | Insufficient indent treated as a new paragraph |
| Missing table delimiter | Only one row |a|b| | Add |---| on row 2 | No delimiter row degrades to plain text |
Rendering safety: XSS scenarios
Note: Any environment that allows raw HTML passthrough (for example an unsanitized CommonMark configuration) can introduce XSS when rendering user-submitted content. This previewer avoids it by design through escape-then-render plus a URL protocol allowlist. The table contrasts three inputs under the two environments:
| Scenario | Risk if raw HTML allowed | This previewer (escape-then-render) | Result |
|---|---|---|---|
<script>alert(1)</script> | Script execution | Escaped to text | Safe |
<img src=x onerror=alert(1)> | Event execution | Escaped to text | Safe |
[x](javascript:alert(1)) | Protocol execution | sanitizeUrl rejects | Renders as plain text |
<style> tag | Layout breakage | Escaped | Safe |
data: URI link | Possible execution | Rejected | Plain text |
To strip the rendered HTML back down to plain text, pair this with the HTML tag stripper.
Volume compared with plain text / HTML
The same content (heading, bold, list, table, code block) expressed four ways, in UTF-8 bytes. Markdown's "symbols are semantics" keeps it most compact; hand-written HTML and rendered HTML balloon about 2x because of tags; plain text is lightest but loses all structure.
| Form | Bytes (UTF-8) | Relative to Markdown | Note |
|---|---|---|---|
| Markdown source | 188 | 1.00× | Most compact, symbols are semantics |
| Hand-written HTML | 359 | 1.91× | Same content, manual tags |
| This previewer rendered HTML | 374 | 1.99× | Semantic tags add slight size |
| Plain text (no markup) | 136 | 0.72× | Content only, no structure |
To compare the Markdown source and its rendered output character by character, see the text diff guide.
Three worked examples (default input)
All three examples use the exact default input produced by the "Load Sample" button. The numbers come from the same logic the page runs, so they match what you see in the editor.
Example 1: word count of the default sample
Clicking "Load Sample" fills the editor with a Markdown block of 491 bytes (UTF-8). The counter below shows 106 words (94 CJK characters + 12 English words). That number comes from countWords as "CJK characters + English words" and matches the live display. To count a longer document on its own, use the word counter; for more on word counts and reading time, see the word counter guide.
Example 2: emphasis and code spans
The second line of the sample, 支持 **粗体**、*斜体* 和 `行内代码`。, renders as:
支持 <strong>粗体</strong>、<em>斜体</em> 和 <code>行内代码</code>。
This shows **粗体** and *斜体* recognized, while the inline code span protects its backtick content. Confirm the code-span shield for emphasis markers: typing 模板 `a**b**c` 中的星号 renders as 模板 <code>a**b**c</code> 中的星号 — the asterisks are not treated as syntax because code spans always keep content literal.
Example 3: link safety allowlist
The last line of the sample, [工具箱里](https://www.calc-tools.top/), passes sanitizeUrl and renders as <a href="https://www.calc-tools.top/">工具箱里</a> (https is on the allowlist). If you instead write [恶意](javascript:alert(1)), sanitizeUrl returns empty and the link is filtered to plain text; mailto:a@b.com is allowed while data: is rejected. Local rendering is therefore immune to XSS.
Common use cases
Markdown is widely used for README files, blog posts, knowledge bases, code documentation, forum posts and chat messages. With this previewer you can quickly check how your writing will look before publishing, verify that tables and code blocks render correctly, and estimate the length of your document before you commit it to a repository or a publishing system.
How to use the result
The live preview shows you, instantly, how the Markdown you wrote will look: heading levels, bold and italic, list indentation, code highlighting and table alignment are all visible at a glance. It fits README files, blog drafts, technical docs, notes and any writing scenario where a what-you-see-is-what-you-get check helps. When you are happy, copy the Markdown source into your platform or tool to publish; the preview only confirms the final effect.
Frequently asked questions
Does a trailing two-space hard break actually work in this previewer? No. In CommonMark, two trailing spaces make a hard break (a <br>), but this previewer collapses intra-paragraph newlines into a single space and emits no <br>. To break a line, start a new paragraph with a blank line.
Why did my pipe table render as a single line of pipe characters instead of a table? A pipe table requires a delimiter row. The correct form puts a row like | --- | --- | right after the header; if you only have one row |a|b| with no delimiter, the renderer treats it as an ordinary paragraph. The leading and trailing | are optional, but the delimiter row is not.
Will pasted <script> or <img src=x onerror=...> execute in the preview? No. This previewer HTML-escapes all text before parsing Markdown, so <script> and onerror appear as plain text and no code runs; javascript: and data: link protocols are also filtered.
Do GFM task lists - [ ] and strikethrough ~~text~~ work in this previewer? Not in the current subset. Task lists and strikethrough are GFM extensions, not part of the CommonMark base spec; this previewer aligns with CommonMark base syntax plus a common subset (headings, emphasis, code, lists, blockquotes, links, tables, horizontal rules). If your target platform uses GFM, preview there to confirm those extensions render.