MdBin Levels Up: From Custom Markdown Pipeline to Streamdown


The Evolution Continues

A few weeks ago, I introduced MdBin—a free utility for sharing beautifully rendered markdown. The response was incredible, and people were using it exactly how I’d hoped: sharing LLM outputs, documentation snippets, and formatted notes without the friction of paywalls or mangled formatting.

But here’s the thing about building in public: you never stop iterating.

Today I’m excited to share a significant upgrade under the hood—migrating from a custom markdown-it + Shiki pipeline to Streamdown, Vercel’s new drop-in replacement for react-markdown.

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<string> {
  const html = await md.renderAsync(content)
  return html
}

Then in my page component, I’d render with the classic dangerouslySetInnerHTML:

<article
  className="markdown-content"
  dangerouslySetInnerHTML={{ __html: renderedContent }}
/>

This approach had several drawbacks:

  1. Manual security handlingdangerouslySetInnerHTML is exactly what it sounds like
  2. No built-in controls — I had to build copy buttons, download functionality myself
  3. Mermaid was a nightmare — SSR rendering just didn’t work reliably
  4. Bundle size creep — Every language highlight added weight
  5. 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

Wait. This is 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
      className="markdown-content"
      parseIncompleteMarkdown
      shikiTheme={['github-light', 'github-dark']}
      mode="streaming"
      isAnimating={false}
      controls={{
        table: true,
        code: true,
        mermaid: {
          download: true,
          copy: true,
          fullscreen: true,
          panZoom: true,
        },
      }}
    >
      {decompressedContent}
    </Streamdown>
  )
}

That’s it. No separate render function. No dangerouslySetInnerHTML. No manual security sanitization.

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 some massive improvements:

🚀 98% Bundle Size Reduction via CDN

This was the big one. Previously, bundling all those Shiki language grammars and themes bloated your 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

This was a blocker for me before. 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 sanitization 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-optimized 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, and it just works.

Try It Out

Head over to mdbin.sivaramp.com 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

Here’s a 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

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 mdbin.sivaramp.com and the Streamdown repo.