Render GitHub-flavored Markdown with Mermaid diagrams in a clean, ChatGPT-style light theme. Type on the left, watch it render on the right, and export to PDF in one click.
- Split view: a CodeMirror editor beside a live preview.
- GitHub-flavored Markdown: tables, task lists, fenced code with syntax highlighting.
- Mermaid diagrams render to vector SVG inside a card that matches ChatGPT's.
- Export to PDF through a server-side Puppeteer route. Text stays selectable and diagrams stay sharp.
- A blog under
/blog, written in markdown and rendered by the same pipeline, so posts can usemermaidblocks. - shadcn-style interface: neutral palette, Geist font, minimal chrome.
bun install
bunx puppeteer browsers install chrome # one-time, for PDF export
bun run devOpen http://localhost:3010.
One unified pipeline in lib/render-markdown.ts turns Markdown into HTML. The browser preview, the blog and the /api/pdf route all call it, so a diagram looks the same everywhere. The PDF route wraps that HTML in a print template, loads it in headless Chromium, waits for Mermaid to finish, and returns the printed page.
markdown ─▶ renderMarkdown() ─▶ HTML ─┬─▶ preview (mermaid runs in the browser)
├─▶ /blog (same browser renderer)
└─▶ /api/pdf ─▶ Puppeteer ─▶ document.pdf
Two rules keep diagrams on screen, both learned from cards that came out empty:
DiagramHostis memoised. Without it, typing re-rendered the component on every keystroke, React re-applied the container's HTML, and every rendered SVG was destroyed and redrawn.- The print template inlines Mermaid from
node_modulesinstead of fetching a CDN, and reveals a diagram's source if rendering fails. A card never prints blank.
Blog posts live in content/blog/*.md with title, description and date frontmatter. Adding a file publishes a post, adds it to the sitemap, and generates its share image.
Local development uses Chrome installed by puppeteer. On Vercel, the PDF route automatically uses the bundled @sparticuz/chromium browser through puppeteer-core. Keep their Chromium major versions aligned when upgrading. next.config.ts traces the browser binaries, mermaid.min.js, and shared stylesheets into the function.
Next.js 16 (App Router), React 19, TypeScript, Tailwind CSS 4, unified/remark/rehype, Mermaid 11, CodeMirror 6, Puppeteer.
