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.
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.