Streamdown

Alternative markdown renderer with built-in syntax highlighting, math, and diagram support.

Streamdown Rendering

This is a paragraph with bold text, italic text, and .

  • First item
  • Second item
  • Third item

This is a blockquote with some quoted text.

NameValue
Alpha100
Beta200

@assistant-ui/react-streamdown is an alternative to @assistant-ui/react-markdown. Choose based on your needs:

  • react-markdown: Lightweight, bring-your-own syntax highlighter
  • react-streamdown: Feature-rich with built-in Shiki, KaTeX, Mermaid support

Installation

npm install @assistant-ui/react-streamdown streamdown

For additional features, install the optional plugins:

npm install @streamdown/code @streamdown/math @streamdown/mermaid @streamdown/cjk

Basic Usage

import { StreamdownTextPrimitive } from "@assistant-ui/react-streamdown";

// Inside a MessagePrimitive.Parts component
<MessagePrimitive.Parts components={{ Text: StreamdownText }} />

// Where StreamdownText is:
const StreamdownText = () => <StreamdownTextPrimitive />;
import { StreamdownTextPrimitive } from "@assistant-ui/react-streamdown";
import { code } from "@streamdown/code";
import { math } from "@streamdown/math";
import { mermaid } from "@streamdown/mermaid";
import "katex/dist/katex.min.css";

const StreamdownText = () => (
  <StreamdownTextPrimitive
    plugins={{ code, math, mermaid }}
    shikiTheme={["github-light", "github-dark"]}
  />
);

When @streamdown/code is provided, the default theme is ["github-light", "github-dark"] for light/dark mode support.

Migration from react-markdown

If you're migrating from @assistant-ui/react-markdown, your existing SyntaxHighlighter and CodeHeader components still work:

import { StreamdownTextPrimitive } from "@assistant-ui/react-streamdown";

const StreamdownText = () => (
  <StreamdownTextPrimitive
    components={{
      SyntaxHighlighter: MySyntaxHighlighter,
      CodeHeader: MyCodeHeader,
    }}
    componentsByLanguage={{
      mermaid: { SyntaxHighlighter: MermaidRenderer }
    }}
  />
);

Props

PropTypeDefaultDescription
mode"streaming" | "static""streaming"Rendering mode
pluginsPluginConfig-Streamdown plugins (code, math, mermaid, cjk)
shikiTheme[string, string]["github-light", "github-dark"]Light and dark theme for Shiki
componentsobject-Custom components including SyntaxHighlighter and CodeHeader
componentsByLanguageobject-Language-specific component overrides
preprocess(text: string) => string-Text preprocessing function
controlsboolean | objecttrueEnable/disable UI controls for code blocks and tables
caret"block" | "circle"-Streaming caret style
mermaidMermaidOptions-Mermaid diagram configuration
linkSafetyLinkSafetyConfig-Link safety confirmation dialog
remendRemendConfig-Incomplete markdown auto-completion
allowedTagsRecord<string, string[]>-HTML tags whitelist
containerPropsobject-Props for the container div
containerClassNamestring-Class name for the container
remarkRehypeOptionsobject-Options passed to remark-rehype
BlockComponentComponentType-Custom component for rendering blocks
parseMarkdownIntoBlocksFn(md: string) => string[]-Custom block parsing function
parseIncompleteMarkdownbooleanfalseParse incomplete markdown as-is (skip remend)
securitySecurityConfig-URL/image security restrictions

Plugin Configuration

Code Highlighting

import { code } from "@streamdown/code";

<StreamdownTextPrimitive
  plugins={{ code }}
  shikiTheme={["github-light", "github-dark"]}
/>

Math (LaTeX)

import { math } from "@streamdown/math";
import "katex/dist/katex.min.css";

<StreamdownTextPrimitive plugins={{ math }} />

Mermaid Diagrams

import { mermaid } from "@streamdown/mermaid";

<StreamdownTextPrimitive plugins={{ mermaid }} />

CJK Text Optimization

import { cjk } from "@streamdown/cjk";

<StreamdownTextPrimitive plugins={{ cjk }} />

Advanced Configuration

Mermaid Options

Customize Mermaid diagram rendering with configuration and error handling:

import { mermaid } from "@streamdown/mermaid";

<StreamdownTextPrimitive
  plugins={{ mermaid }}
  mermaid={{
    config: { theme: "dark" },
    errorComponent: ({ error, chart, retry }) => (
      <div>
        <p>Failed to render diagram: {error}</p>
        <button onClick={retry}>Retry</button>
      </div>
    ),
  }}
/>

Streaming Caret

Display a caret indicator during streaming:

<StreamdownTextPrimitive caret="block" />  // ▋
<StreamdownTextPrimitive caret="circle" /> // ●

Show confirmation before opening external links:

<StreamdownTextPrimitive
  linkSafety={{
    enabled: true,
    onLinkCheck: (url) => url.startsWith("https://trusted.com"),
  }}
/>

Incomplete Markdown Handling (Remend)

Configure how incomplete markdown syntax is handled during streaming:

<StreamdownTextPrimitive
  remend={{
    links: true,      // Complete incomplete links
    images: true,     // Handle incomplete images
    bold: true,       // Complete **text → **text**
    italic: true,     // Complete *text → *text*
    inlineCode: true, // Complete `code → `code`
    katex: true,      // Complete $$equation → $$equation$$
  }}
/>

Allowed HTML Tags

Allow specific HTML tags in markdown content:

<StreamdownTextPrimitive
  allowedTags={{
    div: ["class", "id"],
    span: ["class", "style"],
    iframe: ["src", "width", "height"],
  }}
/>

Security Configuration

Restrict allowed URLs for links and images. This overrides streamdown's default allow-all policy:

<StreamdownTextPrimitive
  security={{
    // Only allow links to trusted domains
    allowedLinkPrefixes: ["https://example.com", "https://docs.example.com"],
    // Only allow images from your CDN
    allowedImagePrefixes: ["https://cdn.example.com"],
    // Restrict protocols
    allowedProtocols: ["https", "mailto"],
    // Disable base64 data images
    allowDataImages: false,
    // CSS class for blocked elements
    blockedLinkClass: "blocked-link",
    blockedImageClass: "blocked-image",
  }}
/>

Detecting Inline vs Block Code

When building custom code components, you can use useIsStreamdownCodeBlock to detect whether you're inside a code block or inline code:

import { useIsStreamdownCodeBlock } from "@assistant-ui/react-streamdown";

function MyCodeComponent({ children, ...props }) {
  const isCodeBlock = useIsStreamdownCodeBlock();

  if (!isCodeBlock) {
    return <code className="inline-code" {...props}>{children}</code>;
  }

  return <pre><code {...props}>{children}</code></pre>;
}

You can also use useStreamdownPreProps to access the props passed to the parent <pre> element:

import { useStreamdownPreProps } from "@assistant-ui/react-streamdown";

function MyCodeComponent({ children }) {
  const preProps = useStreamdownPreProps();

  if (!preProps) {
    // Inline code
    return <code>{children}</code>;
  }

  // Block code - preProps contains className, node, etc.
  return <code className={preProps.className}>{children}</code>;
}

Comparison with react-markdown

Featurereact-markdownreact-streamdown
Bundle sizeSmallerLarger (with plugins)
Syntax highlightingBring your ownBuilt-in Shiki
Math renderingManual setupBuilt-in KaTeX
Mermaid diagramsManual setupBuilt-in support
CJK optimizationNoneBuilt-in
Streamingsmooth propmode + block-based

Re-exported Utilities

The package re-exports useful utilities:

import {
  // Context for accessing streamdown state
  StreamdownContext,
  // Parse markdown into blocks (for custom implementations)
  parseMarkdownIntoBlocks,
  // Hooks for custom code components
  useIsStreamdownCodeBlock,
  useStreamdownPreProps,
  // Memo comparison utility for custom components
  memoCompareNodes,
  // Default Shiki theme: ["github-light", "github-dark"]
  DEFAULT_SHIKI_THEME,
} from "@assistant-ui/react-streamdown";

Available Types

import type {
  // Component props
  StreamdownTextPrimitiveProps,
  SyntaxHighlighterProps,
  CodeHeaderProps,
  ComponentsByLanguage,
  StreamdownTextComponents,
  StreamdownProps,
  // Plugin types
  PluginConfig,
  ResolvedPluginConfig,
  CodeHighlighterPlugin,
  DiagramPlugin,
  MathPlugin,
  CjkPlugin,
  HighlightOptions,
  // Shiki types
  BundledTheme,
  BundledLanguage,
  // Configuration types
  CaretStyle,
  ControlsConfig,
  MermaidOptions,
  MermaidErrorComponentProps,
  LinkSafetyConfig,
  LinkSafetyModalProps,
  RemendConfig,
  RemendHandler,
  AllowedTags,
  RemarkRehypeOptions,
  BlockProps,
  SecurityConfig,
} from "@assistant-ui/react-streamdown";