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:
[email protected]- Core markdown renderer@streamdown/[email protected]- Syntax highlighting (Shiki)@streamdown/[email protected]- Mermaid diagrams
Removed:
[email protected]- No longer needed
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
dangerouslySetInnerHTMLwarnings - Build succeeds (no type errors)
- Performance (should be faster with static mode)
Future Enhancements
Optional Plugins (install if needed):
-
Math Rendering:
bun add @streamdown/math katex -
CJK Support:
bun add @streamdown/cjk -
AI Streaming Mode:
- Switch from
mode="static"to default streaming - Use with Vercel AI SDK for real-time AI markdown generation
- Switch from
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
- Streamdown Docs: https://streamdown.ai/docs/usage
- GitHub: https://github.com/pmndrs/streamdown
- Next.js Server/Client: https://nextjs.org/docs/app/getting-started/server-and-client-components
Next Steps:
- Monitor build logs → Should succeed without errors
- Test articles on production → Check syntax highlighting + Mermaid
- Optional: Add math rendering if needed (
@streamdown/math)
Migration complete! 🚀