Markdown Previewer

Live Markdown-to-HTML rendering in React

Try it!

Type Markdown on the left and see the rendered HTML update live on the right. Supports full CommonMark plus GFM extensions: tables, strikethrough, task lists, and autolinks.


Open in full page ↗

Description


A real-time Markdown previewer built with React 19 and Vite. The interface splits the screen into two equal panes — a monospace textarea on the left and a live HTML preview on the right — with the header taking the top 15% of the viewport. Markdown is parsed by react-markdown v10 with the remark-gfm plugin, adding GitHub Flavored Markdown on top of CommonMark: tables with alternating row shading, strikethrough text, interactive task-list checkboxes, and bare URL autolinks. The preview panel styles all standard elements — headings, blockquotes, fenced code blocks, inline code, lists, and horizontal rules — using CSS variables so the theme adapts automatically to the user's light/dark preference.

How It Works


Markdown → HTML Pipeline (remark / rehype) ═════════════════════════════════════════════ Raw Markdown string (textarea value) │ ▼ ┌───────────────┐ │ remark-parse │ text → MDAST (Markdown AST) └───────┬───────┘ Heading, Paragraph, Code, List... │ ▼ ┌───────────────┐ │ remark-gfm │ extend AST with GFM nodes: └───────┬───────┘ Table, Strikethrough, TaskListItem, Autolink │ ▼ ┌───────────────┐ │ remark-rehype │ MDAST → HAST (Hypertext AST) └───────┬───────┘ element{tag, props, children}, text, root │ ▼ ┌───────────────┐ │ rehype-react │ HAST → React elements └───────┬───────┘ │ ▼ Live preview pane — re-renders on every keystroke No raw innerHTML — the remark/rehype pipeline produces React elements, so XSS is not possible.

Split-Pane Layout ══════════════════ ┌────────────────────────────────────────┐ ▲ │ Header │ │ 15vh ├────────────────────┬───────────────────┤ ▼ │ Textarea │ Preview pane │ ▲ │ (monospace) │ (styled HTML) │ │ │ 50% width │ 50% width │ │ 85vh │ overflow-y: auto │ overflow-y: auto│ ▼ └────────────────────┴───────────────────┘ CSS variable scoping bug (fixed): ───────────────────────────────── :root vars defined globally → work in textarea ✓ react-markdown renders into div → vars not inherited ✗ fix: pass vars as inline style on the preview div ✓ Both panes scroll independently. CSS variables had to be explicitly forwarded into the preview container.

In Action

Editor proof points

  • Split-pane layout keeps Markdown input and rendered preview visible together.
  • Independent scrolling prevents long documents from shifting the whole page.
  • Vite and React keep the editor responsive while content updates live.
Split-pane editor with live preview

Markdown rendering proof points

  • react-markdown handles the AST-to-React render path.
  • remark-gfm adds tables, task lists, and strikethrough support.
  • Preview styling is scoped so generated content inherits the intended theme.
GFM table rendering

Dev Notes

Problems Solved

Needed a fast, clean Markdown editor with live preview that supported full GFM — tables, task lists, strikethrough — without building a parser from scratch. The split-pane layout needed to work at any viewport size without layout shift as you type.

Errors & Fixes

react-markdown v10 changed its plugin API from v9 — the rehype plugin chain required explicit configuration to work alongside remark-gfm. CSS variables defined at the root didn't scope into the preview panel's rendered HTML; fixed by re-applying them as inline styles on the preview container.

What I Learned

The remark/rehype plugin ecosystem and how react-markdown processes AST nodes before rendering. Vite's CSS handling quirks, and deploying a React + Vite app to Vercel with correct SPA routing config so direct URL loads don't 404.


Back to top