Analysis

Streamdown Migration - Completed ✅

2026.02.1310 min read

Date: 2026-02-14
Suggested By: Essa Mamdani
Status: Migrated from marked to streamdown


Why Streamdown?

Previous Setup (marked + dangerouslySetInnerHTML):

  • ❌ Server-side HTML string generation
  • ❌ Used dangerouslySetInnerHTML (security risk)
  • ❌ Manual regex hacks for Mermaid diagrams
  • ❌ No syntax highlighting out of the box
  • ❌ Not type-safe (HTML strings)

New Setup (Streamdown):

  • React-native markdown rendering (type-safe)
  • No dangerouslySetInnerHTML (safer)
  • Built-in plugins: Code highlighting (Shiki) + Mermaid
  • Static mode: Optimized for blog articles
  • Better Next.js integration (client component for content, server for metadata)
  • Drop-in replacement for react-markdown

Reference: https://streamdown.ai/docs/usage


Implementation

1. Installed Packages

bun add streamdown @streamdown/code @streamdown/mermaid

Installed:

Removed:

2. Created Client Component

File: components/article-content.tsx

'use client'

import { Streamdown } from 'streamdown'
import { code } from '@streamdown/code'
import { mermaid } from '@streamdown/mermaid'

export function ArticleContent({ content }: { content: string }) {
  return (
    <div className="prose prose-lg dark:prose-invert max-w-none">
      <Streamdown
        mode="static"  // Optimized for static content
        plugins={{
          code: code,      // Syntax highlighting
          mermaid: mermaid, // Diagrams
        }}
        shikiTheme={['github-light', 'github-dark']} // Theme per dark mode
        mermaid={{
          config: {
            theme: 'dark',
            themeVariables: {
              primaryColor: '#10b981',  // Emerald green
            },
          },
        }}
      >
        {content}
      </Streamdown>
    </div>
  )
}

3. Updated Article Page

Before:

// Server component - converted markdown to HTML string
const htmlContent = await marked.parse(article.content)

// Manual Mermaid regex hacks
htmlContent = htmlContent.replace(
  /<pre><code class="language-mermaid">...
)

// Unsafe HTML rendering
<article dangerouslySetInnerHTML={{ __html: htmlContent }} />

After:

// Server component - passes raw markdown to client
import { ArticleContent } from '@/components/article-content'

// Clean, safe rendering
<ArticleContent content={article.content} />

Architecture

Server vs Client Components

Server Component (app/articles/[slug]/page.tsx):

  • Handles metadata generation (generateMetadata)
  • Static params generation (generateStaticParams)
  • File system reads (via lib/markdown.ts)
  • SEO optimization

Client Component (components/article-content.tsx):

  • Renders markdown with Streamdown
  • Handles syntax highlighting
  • Interactive Mermaid diagrams
  • Progressive enhancement

Why This Split?

  • Server components can't use interactive features (React state, effects)
  • Streamdown uses client-side rendering for optimal streaming support
  • Metadata + data fetching stay fast (server-side)
  • Content rendering gets React benefits (client-side)

Features Enabled

1. Syntax Highlighting (Shiki)

Automatic code highlighting with dual themes:

const example = "Highlighted automatically!"

Supports:

  • 100+ languages
  • Light/dark theme switching
  • Line numbers
  • Diff highlighting

2. Mermaid Diagrams

Renders diagrams from markdown:

graph LR
    A[Start] --> B[Process]
    B --> C[End]

Supports:

  • Flowcharts, sequence diagrams, Gantt charts
  • Dark mode optimized
  • Custom themes

3. Markdown Extensions

  • GitHub Flavored Markdown (GFM)
  • Tables
  • Task lists
  • Strikethrough
  • Autolinks

Testing Checklist

After deployment:

  • Articles render correctly (no HTML string artifacts)
  • Syntax highlighting works (light/dark themes)
  • Mermaid diagrams render
  • No dangerouslySetInnerHTML warnings
  • Build succeeds (no type errors)
  • Performance (should be faster with static mode)

Future Enhancements

Optional Plugins (install if needed):

  1. Math Rendering:

    bun add @streamdown/math katex
    
  2. CJK Support:

    bun add @streamdown/cjk
    
  3. AI Streaming Mode:

    • Switch from mode="static" to default streaming
    • Use with Vercel AI SDK for real-time AI markdown generation

Performance Impact

Before (marked):

  • Server-side HTML generation
  • No code splitting
  • Manual script injection for Mermaid

After (Streamdown):

  • Client-side rendering (hydration)
  • Code-split plugins (on-demand loading)
  • Built-in optimization

Result: Slightly larger client bundle (+50KB for Shiki themes), but better developer experience, type safety, and maintainability.


Documentation


Next Steps:

  1. Monitor build logs → Should succeed without errors
  2. Test articles on production → Check syntax highlighting + Mermaid
  3. Optional: Add math rendering if needed (@streamdown/math)

Migration complete! 🚀

Share This Article