MdBin Levels Up: From Custom Markdown Pipeline to Streamdown

Published: (January 16, 2026 at 10:08 PM EST)
5 min read
Source: Dev.to

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 handlingdangerouslySetInnerHTML is 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

FeatureBeforeAfter
SecurityManual with dangerouslySetInnerHTMLBuilt‑in rehype-harden
Code copyCustom CopyButton component✅ Built‑in
Mermaid SSR❌ Broken✅ Works
Table download❌ Not implemented✅ Built‑in
Math rendering❌ Not implemented✅ KaTeX built‑in
Bundle sizeGrowing with each languageCDN‑loaded on demand
Incomplete markdownCould break renderGraceful 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.

Cover image for MdBin Levels Up: From Custom Markdown Pipeline to Streamdown

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

Back to Blog

Related posts

Read more »

The first four

Follow‑up on Raku Resolutionshttps://dev.to/lizmat/raku-resolutions-17g7 The first meeting was held at the suggested time and date: 17 January 2026 at 19:00 UT...

LuxDev Markdown Language Class

How to write using markdown language Headers - sign makes the text a title - two hash signs make the text a subtitle - three hash signs make it a subsection un...