MdBin Levels Up: From Custom Markdown Pipeline to Streamdown
Source: Dev.to
The Old Setup: It Worked, But
My original implementation was solid. I used markdown-it-async with @shikijs/markdown-it for syntax highlighting:
import { fromAsyncCodeToHtml } from '@shikijs/markdown-it/async'
import MarkdownItAsync from 'markdown-it-async'
import { codeToHtml } from 'shiki'
const md = MarkdownItAsync({
html: true,
xhtmlOut: true,
linkify: true,
typographer: true,
breaks: true,
})
md.use(
fromAsyncCodeToHtml(codeToHtml, {
themes: {
light: 'github-light',
dark: 'github-dark',
},
defaultColor: false,
cssVariablePrefix: '--shiki-',
})
)
export async function renderMarkdown(content: string): Promise {
const html = await md.renderAsync(content)
return html
}
Then in my page component I rendered the result with the classic dangerouslySetInnerHTML:
Drawbacks
- Manual security handling —
dangerouslySetInnerHTMLis exactly what it sounds like. - No built‑in controls — I had to build copy buttons, download functionality myself.
- Mermaid was a nightmare — SSR rendering just didn’t work reliably.
- Bundle‑size creep — Every language highlight added weight.
- Incomplete block handling — Malformed markdown could break the entire render.
Enter Streamdown
When I discovered Streamdown, I initially dismissed it. “It’s for AI streaming,” I thought. “MdBin renders static content SSR.”
But then I looked closer at the feature set:
- 🎯 Code syntax highlighting with Shiki (what I was already using)
- 📈 Mermaid diagrams that actually work SSR
- 🛡️ Security‑first with
rehype-harden - 📊 GitHub‑flavored markdown out of the box
- 🔢 Math rendering via KaTeX
- ⚡ Built‑in controls for copy, download, fullscreen
That’s exactly what a markdown‑sharing tool needs.
The Migration: Surprisingly Painless
Here’s what the new implementation looks like:
import { Streamdown } from 'streamdown'
export default async function PastePage({ params }) {
const { id } = await params
const { decompressedContent, createdAt } = await cachedGetPaste(id)
return (
<Streamdown mode="static">
{decompressedContent}
</Streamdown>
)
}
That’s it. No separate render function, no dangerouslySetInnerHTML, no manual security sanitisation.
The Tailwind setup is one line in globals.css:
@source "../../node_modules/streamdown/dist/index.js";
Why v2.0.0 Is a Game Changer
The timing couldn’t have been better. Streamdown just shipped v2.0.0 with massive improvements:
🚀 98 % Bundle‑Size Reduction via CDN
Previously, bundling all those Shiki language grammars and themes bloated the build. Now language‑specific highlighting and KaTeX CSS are loaded from their CDN on‑demand. Your build stays lean; users only fetch what they need.
📈 Mermaid SSR Actually Works
Mermaid diagrams in markdown are incredibly useful—flowcharts, sequence diagrams, architecture docs—but SSR rendering was broken. Now it works beautifully, with viewport‑based lazy loading that prevents page freezing when you have multiple diagrams.
🛡️ Enhanced Security
rehype-harden + rehype-sanitize means I don’t have to worry about XSS attacks from malicious markdown. The component handles sanitisation automatically.
✨ Built‑in UX Polish
All those controls I would have had to build myself? Built‑in:
- Code blocks – copy button, language indicator
- Tables – download as CSV
- Mermaid – download as PNG, copy SVG, fullscreen view with pan/zoom
What I Gained
| Feature | Before | After |
|---|---|---|
| Security | Manual with dangerouslySetInnerHTML | Built‑in rehype-harden |
| Code copy | Custom CopyButton component | ✅ Built‑in |
| Mermaid SSR | ❌ Broken | ✅ Works |
| Table download | ❌ Not implemented | ✅ Built‑in |
| Math rendering | ❌ Not implemented | ✅ KaTeX built‑in |
| Bundle size | Growing with each language | CDN‑loaded on demand |
| Incomplete markdown | Could break render | Graceful handling |
The “Streaming” Part
You might wonder: “Why use a streaming‑optimised component for static content?” The answer is simple—Streamdown’s streaming mode gives you progressive rendering. The markdown is parsed and streamed to the DOM as it arrives, which means:
- Faster Time‑to‑First‑Contentful‑Paint (especially for large posts).
- The UI remains responsive while heavy diagrams or code blocks are being processed.
- It works equally well for static SSR and for real‑time AI‑generated streams, giving you flexibility for future features.
In short, I got a cleaner codebase, better security, dramatically smaller bundles, and a richer UX—all with virtually no migration effort.

“ed library for static content?”
Fair question. The mode="streaming" and isAnimating={false} props tell Streamdown this is pre‑rendered content—no typing effects, no progressive reveal. But all the other benefits still apply:
parseIncompleteMarkdown– Handles edge cases where users paste malformed markdown (unclosed code blocks, incomplete tables). Instead of crashing or showing garbage, it renders gracefully.- Memoized rendering – Even without streaming, the performance optimizations help with re‑renders.
Shoutout to the Vercel Team
The Streamdown team has done an incredible job packaging what could have been a complex, multi‑library setup into a single, well‑designed component. It powers their AI Elements Message component, but it’s genuinely useful for any markdown‑rendering use case.
- The documentation is solid.
- The defaults are sensible.
- It just works.
Try It Out
Head over to and paste some markdown. Try:
- Code blocks in any language—notice the copy button and language badge.
- Mermaid diagrams—they actually render now! Try the fullscreen + pan/zoom.
- Tables—check out the download button.
- Math equations—LaTeX just works.
Quick Mermaid example to paste
graph TD
A[Paste Markdown] --> B[Streamdown Renders]
B --> C{What type?}
C -->|Code| D[Shiki Highlighting]
C -->|Diagram| E[Mermaid SVG]
C -->|Math| F[KaTeX Render]
D --> G[Beautiful Output]
E --> G
F --> G
- Enter fullscreen mode
- Exit fullscreen mode
What’s Next
With the rendering layer now handled by Streamdown, I can focus on features users actually want:
- Expiration options – 1 hour, 1 day, 1 week, or permanent.
- Password protection – For sensitive content.
- Edit links – Update pastes without creating new ones.
- Custom themes – Beyond light/dark mode.
The foundation is solid. Now it’s time to build.
TL;DR: Migrated MdBin from markdown-it + Shiki to Vercel’s Streamdown. Got built‑in code copy, Mermaid rendering (that actually works SSR!), math support, enhanced security, and a 98 % smaller bundle—all from one component. The Vercel team knocked it out of the park with this one.
Check out the upgrade at and the Streamdown repo.