Skip to content
Data2026-07-149 min read

Online Markdown Preview: Real-time Render and Edit Markdown

What Is Markdown

Markdown is a lightweight markup language proposed by John Gruber in 2004. Readable plain text expresses headings, lists, links, and more; a parser turns that into HTML or other formats. It is the default writing surface for technical docs, READMEs, blogs, and notes.

To move the same content between HTML and Markdown, use the HTML ↔ Markdown converter. To see final layout while writing, use Markdown Preview.

Why Preview Matters

“Looks aligned in the source” is not the same as “renders aligned.” Preview mainly catches three classes of failure:

  1. Structure: table separator column count mismatches, unclosed fences, nested-list indent off by one
  2. Dialect: GFM tables published into a CommonMark-only renderer become literal pipes
  3. Pre-publish checks: task lists, quotes, and links still behave on the destination site

Prose paragraphs rarely need constant preview; tables, nested lists, and fenced code blocks are the high-ROI cases.

How to Use ToolVault Markdown Preview

Open Markdown Preview:

  1. Default view is split: source on the left (Monaco / CodeEditor), live render on the right
  2. Switch to source or preview only; the preference is stored under the local key tool:markdown:view
  3. First load includes a GFM sample (headings, task list, table, quote, fenced code)—edit it to verify behavior
  4. Use Copy HTML in the toolbar for a marked-rendered HTML string (same GFM dialect as the converter). You can still take the Markdown source, or edit both directions in the converter

Rendering stays in the browser; the body is not POSTed for preview.

CommonMark vs GFM: Fidelity Starts Here

Markdown fragmented as soon as it succeeded:

DialectRoleThis preview
Original MarkdownGruber’s description + ambiguous implementationsNot a separate target
CommonMarkShared base for modern parsersPortable core syntax
GFMCommonMark superset: tables, strikethrough, task lists, autolinks, footnotes, …Target dialect (remark-gfm)
Site-specific extensionsFrontmatter, math, admonitions, shortcodesNot guaranteed; preview on the destination

GFM tables on a pure CommonMark renderer show up as pipe characters. Task lists on an old wiki that ignores them degrade to ordinary list text.

MediaWiki, some legacy CMSes, and chat Markdown subsets need a second preview on that system. This tool matches developer-docs GFM, not the union of every dialect.

Syntax Supported Here

# Heading 1
## Heading 2

**Bold** and *italic*; inline code uses backticks.

- Unordered item
1. Ordered item

[Link text](https://example.com)
![Alt text](https://example.com/image.png)

> Blockquotes can span lines

Strikethrough (~~text~~) is GFM, not strict CommonMark core.

Fenced code blocks

Put a language tag after the opening fence (for example the tag js). This tool wraps content in monospace pre / code and may expose a language-* class—there is no syntax-highlighter plugin, so you will not see colored keywords. Highlighting needs rehype-highlight / Prism (or the destination docs site).

Tables (GFM)

| Env | Host | Traffic |
|---|---|---:|
| prod | www.example.com | 100% |
| staging | staging.example.com | internal |

Rules of thumb:

  • The |---| separator row must match the header column count, or the table often collapses to plain text
  • Alignment uses colons in the separator: :--- left, ---: right, :---: center
  • Escape | inside cells as \|
  • Space-padding is for humans reading source; renderers do not require aligned spaces

Build tables visually with the Markdown table generator, then paste back here to confirm.

Task lists (GFM)

- [x] Done
- [ ] Todo

Preview shows disabled checkboxes for display. Interactive toggling that writes back into the source is out of scope.

Bare URLs such as https://example.com often become clickable under GFM. Email-literal behavior varies slightly by parser—trust the preview.

Common extensions this tool does not implement

ConstructHere
:smile: emoji shortcodesNot expanded; paste Unicode emoji instead
$E=mc^2$ / KaTeXNo math rendering
Admonition / callout shortcodesUnrecognized (unless written as normal quotes/HTML elsewhere)
YAML frontmatterShown as body text; metadata is not stripped

Safe Rendering and XSS Boundaries

The pipeline is:

source string → remark (+ remark-gfm) → react-markdown React elements → Tailwind Typography (prose) styles

Boundaries aligned with the current dependencies (other sites may differ):

  1. Raw HTML in the Markdown source is not treated as an executable DOM fragment
    react-markdown tends to escape or skip HTML rather than inject via dangerouslySetInnerHTML. Older copy that claims “script tags run in preview” is wrong for this tool. “You can mix HTML” in many platforms describes their dialect—not a guarantee here.

  2. Link and image URLs pass through the default urlTransform
    Dangerous schemes such as javascript: are blocked. Do not disable or replace that transform with a pass-through—that is an XSS hole.

  3. No separate rehype-sanitize allowlist plugin
    Safety comes mainly from the Markdown→React model plus URL transforms, not a second HTML sanitizer. If you paste HTML from the converter into your page with innerHTML, that is a different threat model—use your own sanitizer, or see the HTML entity encoder guide.

  4. Images and external links still have side effects
    ![x](https://…) triggers a browser request. Local preview does not mean “image URLs are free of privacy or mixed-content issues.”

  5. Contrast: converter Markdown → HTML
    The HTML ↔ Markdown tool uses marked.parse to emit an HTML string into an editor (as text), not this React path. Whether that string is safe depends on how you embed it later.

Preview vs publish: two different jobs

Local preview answers “does this GFM document parse the way I think?” Publishing answers a longer checklist: frontmatter metadata, image CDN paths, absolute vs relative links, MDX/JSX rules (a bare less-than sign in prose can break MDX), and site-specific shortcodes. Treat this tool as a syntax and layout smoke test, not a substitute for the destination build.

A practical loop that saves review cycles:

  1. Draft structure here (headings, tables, task lists) until the split view looks right
  2. Paste fenced configs through YAML ↔ JSON or the JSON formatter so examples are valid before they enter the README
  3. Run the destination preview (GitHub PR, docs site, CMS) once for dialect extras this page does not claim

If step 3 fails while step 1 looked fine, the bug is almost always an extension or sanitizer—not headings or bold. Keep a two-line repro of the failing construct (one table row, one nested list) so reviewers are not comparing whole READMEs. When the destination adds plugins (math, admonitions, mermaid), list those explicitly in the PR so nobody treats this browser preview as the final renderer.

Copy HTML vs on-screen preview

Toolbar Copy HTML runs marked (GFM) and puts an HTML string on the clipboard. The right-hand pane still uses react-markdown + remark-gfm. Both target GFM tables and task lists, but they are not the same code path:

PathOutputSafe to paste into a live page?
On-screen previewReact elements under TypographyYes — stays inside React
Copy HTMLHTML string from markedOnly after your sanitizer / CSP policy

Use Copy HTML for email templates, static snippets, or CMS fields that expect markup. Do not treat the clipboard string as “already sanitized for production.”

Frequent Pitfalls

Unclosed fences

A missing closing fence turns the rest of the document into code—the tell is “everything after line N is monospace.”

Nested list indentation

Sub-items usually align with the parent text (commonly 2 or 4 spaces). Mixing tabs and spaces is inconsistent across engines; this page follows CommonMark/GFM—trust the preview.

Hard breaks vs paragraphs

With break-joining semantics, a single newline often stays inside one paragraph. Hard breaks need trailing spaces or a blank line. GitHub comments and some chat clients are more aggressive—do not assume “newline in source = newline in preview.”

GFM footnotes ([^1]) render when the destination supports them; this preview follows remark-gfm footnote rules. Reference-style links ([text][id] with a definition later) are easy to break if the definition line is indented into a list or fenced block—keep definitions at column zero.

When preview and production disagree

  1. Minimize to a two-line repro
  2. Confirm both sides are GFM (especially tables / task lists / strikethrough)
  3. Check whether the destination strips HTML, changes autolinks, or adds math plugins

The culprit is almost always an extension boundary, not core # / ** syntax.

Working with YAML, JSON, Diff, and HTML

ScenarioPath
Config snippets in a READMENormalize with YAML ↔ JSON, then fence; read the YAML JSON guide
API examples in docsJSON formatter before the code block; see the JSON formatting guide
Two versions of a doc/configText Diff; see the text Diff guide
Web page → MarkdownHTML ↔ Markdown, then preview here
Escape text for HTML attributesHTML entity encoder

Markdown preview is not a Diff tool and does not interpret YAML frontmatter semantics.

Implementation Notes

These points match MarkdownTool in src/components/DataTool.tsx:

  • Stack: react-markdown with a single remark plugin, remark-gfm. Not configured: rehype-highlight, remark-math, remark-emoji, rehype-raw, rehype-sanitize.
  • Styling: Tailwind Typography prose prose-sm (including dark), not a separate Markdown CSS framework.
  • Views: split / source / preview; default split; preference via usePersistentState.
  • Copy HTML uses marked (GFM); the on-screen preview still uses react-markdown + remark-gfm. Treat copied HTML as text until you sanitize it for your own page.
  • No colored syntax highlighting: language tags may appear as classes; the page does not colorize tokens.
  • Emoji shortcodes are not expanded: use Unicode characters.
  • Sample document: built-in Chinese GFM checklist sample to show tables and task lists on first paint—not lorem.
  • Relation to this site’s blog: blog MDX serialization also enables remark-gfm (pages/blog/[slug].tsx), so table dialect matches between posts and this tool. Blog content additionally follows MDX/JSX rules (a bare less-than sign in prose can break serialization)—a different path from this pure Markdown string preview.

FAQ

Can Markdown and HTML be mixed?

Many platforms allow HTML inside Markdown. This preview tool does not treat raw HTML in the source as an executable page fragment. For an HTML string, use the converter and sanitize before embedding on your own site.

What file extension?

Usually .md or .markdown. GitHub, GitLab, and similar platforms render .md in repositories.

Math support?

Neither standard Markdown nor this preview ships KaTeX/MathJax. Enable math on the destination platform or render after export.

Is data uploaded?

Preview runs locally in the browser. View preferences may land in local storage; on shared machines, clear site data or use a private window when finished.

Why does a “perfectly aligned” table break in preview?

Count separator pipes against the header column count, then check for unescaped | in cells. Misaligned spaces rarely matter; column-count mismatches do.

Further Reading


From the ToolVault developer notebook. The homepage collects more browser-local tools.