Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
bjornmelin avatar

Streamdown

  • 17 installs
  • 5 repo stars
  • Updated August 5, 2026
  • bjornmelin/dev-skills

Streamdown is a Claude Code skill for Vercel's Streamdown library, a streaming-optimized react-markdown replacement for rendering AI-generated markdown in React chat UIs.

About

Streamdown is guidance for Vercel's Streamdown library, a react-markdown replacement built for streaming AI output. It shows how to render AI-generated markdown in React chat UIs while gracefully handling incomplete syntax during streaming. A developer uses it when building chat interfaces with the AI SDK useChat/streamText, or migrating from react-markdown. It covers Shiki code themes, KaTeX math, Mermaid diagrams, styling with Tailwind, and hardening output with rehype-harden.

  • Drop-in react-markdown replacement that handles incomplete markdown during AI streaming via the remend preprocessor
  • Configures Shiki code themes, KaTeX math, and Mermaid diagrams with copy/download controls
  • Integrates with AI SDK useChat status for isAnimating and rehype-harden for safer AI output

Streamdown by the numbers

  • 17 all-time installs (skills.sh)
  • Ranked #1,582 of 2,245 Frontend Development skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
At a glance

streamdown capabilities & compatibility

Capabilities
render markdown · syntax highlighting · math rendering · diagram rendering · ai chat ui
Works with
vercel · openai · anthropic
Use cases
frontend · ui design
IDEs
vscode · cursor ide
From the docs

What streamdown says it does

Streamdown is a drop-in react-markdown replacement designed for AI-powered streaming applications.
SKILL.md
It handles incomplete markdown syntax gracefully using the remend preprocessor.
SKILL.md
The `status` from useChat maps directly to Streamdown's `isAnimating`:
SKILL.md
npx skills add https://github.com/bjornmelin/dev-skills --skill streamdown

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs17
repo stars5
Last updatedAugust 5, 2026
Repositorybjornmelin/dev-skills

What it does

Render streaming AI-generated markdown in React chat UIs, handling incomplete markdown, code, math, and diagrams.

Who is it for?

Rendering incomplete, streaming AI markdown in React chat interfaces built on the AI SDK.

Skip if: Non-React apps or static markdown rendering where streaming is irrelevant.

When should I use this skill?

Rendering AI-generated markdown from useChat/streamText or migrating from react-markdown to streaming-friendly rendering.

What you get

A React chat UI that renders streaming markdown, code, math, and diagrams without breaking on partial syntax.

  • React markdown rendering component wiring
  • Shiki/KaTeX/Mermaid configuration

By the numbers

  • 12 documented core props

Files

SKILL.mdMarkdownGitHub ↗

Streamdown - AI Streaming Markdown

Streamdown is a drop-in react-markdown replacement designed for AI-powered streaming applications. It handles incomplete markdown syntax gracefully using the remend preprocessor.

Quick Start

Installation

# Direct installation
pnpm add streamdown

# Or via AI Elements CLI (includes Response component)
pnpm dlx ai-elements@latest add message

Tailwind Configuration

Tailwind v4 (globals.css):

@source "../node_modules/streamdown/dist/*.js";

Tailwind v3 (tailwind.config.js):

module.exports = {
  content: [
    './app/**/*.{js,ts,jsx,tsx}',
    './node_modules/streamdown/dist/*.js',
  ],
}

Basic Chat Example

'use client';
import { useChat } from '@ai-sdk/react';
import { Streamdown } from 'streamdown';

export default function Chat() {
  const { messages, sendMessage, status } = useChat();

  return (
    <>
      {messages.map(message => (
        <div key={message.id}>
          {message.parts
            .filter(part => part.type === 'text')
            .map((part, index) => (
              <Streamdown
                key={index}
                isAnimating={status === 'streaming'}
              >
                {part.text}
              </Streamdown>
            ))}
        </div>
      ))}
    </>
  );
}

Core Props

PropTypeDefaultDescription
childrenstringrequiredMarkdown content to render
isAnimatingbooleanfalseDisables interactive controls during streaming
mode`"streaming" \"static"`"streaming"
shikiTheme[BundledTheme, BundledTheme]['github-light', 'github-dark']Light/dark syntax themes
controls`ControlsConfig \boolean`true
mermaidMermaidOptions{}Diagram configuration
componentsobject{}Custom element overrides
classNamestring""Container CSS class
remarkPluginsPluggable[]GFM, math, CJKMarkdown preprocessing
rehypePluginsPluggable[]raw, katex, hardenHTML processing
parseIncompleteMarkdownbooleantrueEnable remend preprocessor

AI SDK Integration

Status-Based isAnimating

The status from useChat maps directly to Streamdown's isAnimating:

const { messages, status } = useChat();
// status: 'submitted' | 'streaming' | 'ready' | 'error'

<Streamdown isAnimating={status === 'streaming'}>
  {content}
</Streamdown>

Message Parts Pattern

AI SDK v6 uses message parts instead of content string:

{messages.map(message => (
  <div key={message.id}>
    {message.parts
      .filter(part => part.type === 'text')
      .map((part, index) => (
        <Streamdown key={index} isAnimating={status === 'streaming'}>
          {part.text}
        </Streamdown>
      ))}
  </div>
))}

Memoized Response Component

Wrap Streamdown with React.memo for performance:

import { memo, ComponentProps } from 'react';
import { Streamdown } from 'streamdown';

export const Response = memo(
  ({ className, ...props }: ComponentProps<typeof Streamdown>) => (
    <Streamdown
      className={cn('prose dark:prose-invert max-w-none', className)}
      {...props}
    />
  )
);

Configuration Examples

Shiki Themes

import type { BundledTheme } from 'shiki';

const themes: [BundledTheme, BundledTheme] = ['github-light', 'github-dark'];

<Streamdown shikiTheme={themes}>{content}</Streamdown>

Controls

<Streamdown
  controls={{
    code: true,           // Copy button on code blocks
    table: true,          // Download button on tables
    mermaid: {
      copy: true,         // Copy diagram source
      download: true,     // Download as SVG
      fullscreen: true,   // Fullscreen view
      panZoom: true,      // Pan/zoom controls
    },
  }}
>
  {content}
</Streamdown>

Mermaid Diagrams

import type { MermaidConfig } from 'streamdown';

const mermaidConfig: MermaidConfig = {
  theme: 'base',
  themeVariables: {
    fontFamily: 'Inter, sans-serif',
    primaryColor: 'hsl(var(--primary))',
    lineColor: 'hsl(var(--border))',
  },
};

<Streamdown mermaid={{ config: mermaidConfig }}>{content}</Streamdown>

Custom Error Component for Mermaid

import type { MermaidErrorComponentProps } from 'streamdown';

const MermaidError = ({ error, chart, retry }: MermaidErrorComponentProps) => (
  <div className="p-4 border border-destructive rounded">
    <p>Failed to render diagram</p>
    <button onClick={retry}>Retry</button>
  </div>
);

<Streamdown mermaid={{ errorComponent: MermaidError }}>{content}</Streamdown>

Custom Components

Override any markdown element:

<Streamdown
  components={{
    h1: ({ children }) => <h1 className="text-4xl font-bold">{children}</h1>,
    a: ({ href, children }) => (
      <a href={href} className="text-primary underline">{children}</a>
    ),
    code: ({ children, className }) => (
      <code className={cn('bg-muted px-1 rounded', className)}>{children}</code>
    ),
  }}
>
  {content}
</Streamdown>

Security Configuration

Restrict protocols for AI-generated content:

import { defaultRehypePlugins } from 'streamdown';
import { harden } from 'rehype-harden';

<Streamdown
  rehypePlugins={[
    defaultRehypePlugins.raw,
    defaultRehypePlugins.katex,
    [harden, {
      allowedProtocols: ['http', 'https', 'mailto'],
      allowedLinkPrefixes: ['https://your-domain.com'],
      allowDataImages: false,
    }],
  ]}
>
  {content}
</Streamdown>

Streaming vs Static Mode

ModeUse CaseFeatures
streamingAI chat responsesBlock parsing, incomplete markdown handling, memoization
staticBlog posts, docsSimpler rendering, no streaming optimizations
// Static mode for pre-rendered content
<Streamdown mode="static">{blogContent}</Streamdown>

Built-in Features

  • GFM: Tables, task lists, strikethrough, autolinks
  • Math: KaTeX rendering with $$...$$ syntax
  • Code: Shiki syntax highlighting (200+ languages)
  • Diagrams: Mermaid with interactive controls
  • CJK: Proper emphasis handling for Chinese/Japanese/Korean
  • Security: rehype-harden for link/image protocol restrictions

Reference Files

ReferenceTopics
api-reference.mdComplete props, types, plugins, data attributes
ai-sdk-integration.mduseChat patterns, server setup, message parts
styling-security.mdTailwind, CSS variables, custom components, rehype-harden

Common Patterns

Next.js Configuration

If you see bundling errors with Mermaid:

// next.config.js
module.exports = {
  serverComponentsExternalPackages: ['langium', '@mermaid-js/parser'],
  webpack: (config, { isServer }) => {
    if (!isServer) {
      config.resolve.alias = {
        ...config.resolve.alias,
        'vscode-jsonrpc': false,
        'langium': false,
      };
    }
    return config;
  },
};

Shiki External Package

// next.config.js
{
  transpilePackages: ['shiki'],
}

Version Notes

  • Streamdown: Works with React 18+ (optimized for React 19)
  • AI SDK: Designed for v6 (status-based streaming state)
  • Tailwind: Supports v3 and v4 configurations

Related skills

FAQ

What does Streamdown replace?

It is a drop-in react-markdown replacement designed for AI-powered streaming applications.

How does it handle incomplete markdown?

It uses the remend preprocessor, enabled via parseIncompleteMarkdown, to handle incomplete syntax during streaming.

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.