Skip to main content
The Streamdown component is the main entry point for rendering Markdown in your application. It handles both static and live-streaming content, parses Markdown into optimally memoized blocks, applies syntax highlighting, animations, and more.
import { Streamdown } from "streamdown";

export default function Chat({ text, isStreaming }) {
  return (
    <Streamdown isAnimating={isStreaming}>
      {text}
    </Streamdown>
  );
}

Props

children
string
The raw Markdown string to render. Streamdown expects a plain string. In streaming mode, pass the full accumulated content on every update — Streamdown will efficiently reconcile the diff.
mode
"static" | "streaming"
default:"streaming"
Controls how Streamdown processes and renders content.
  • "streaming" — Parses the Markdown into discrete blocks, applies incomplete-markdown handling via remend, and uses React Transitions to batch block updates without blocking the UI.
  • "static" — Renders the entire Markdown string in a single pass. Skips incomplete-markdown preprocessing and animation callbacks. Use this for already-complete content.
In "static" mode, onAnimationStart and onAnimationEnd are suppressed even if isAnimating changes.
dir
"auto" | "ltr" | "rtl"
Text direction applied to rendered blocks.
  • "auto" — Detects direction per block using the Unicode first-strong-character algorithm. Each block gets its own dir attribute.
  • "ltr" / "rtl" — Applies the given direction to all blocks.
When omitted, no dir attribute is added and the browser inherits direction from the DOM.
isAnimating
boolean
default:"false"
When true, signals that content is currently being streamed. Used to:
  • Show the caret character (if caret is set)
  • Activate the animated entrance animation on new content
  • Mark the last block as incomplete for components that need it
  • Trigger onAnimationStart / onAnimationEnd callbacks
animated
boolean | AnimateOptions
Enables per-word or per-character entrance animations on newly streamed content. Set to true for defaults, or pass an AnimateOptions object to customize.Already-rendered content is never re-animated when new tokens arrive. Streamdown tracks the previous render’s character count and skips animation for those positions.
Avoid passing an inline object literal for animated on every render (e.g. animated={{ duration: 100 }}). Streamdown stabilizes the value internally using JSON.stringify, so the plugin will not be recreated unnecessarily, but a stable reference is still preferred.
caret
"block" | "circle"
Displays a blinking caret character after the last rendered character when isAnimating is true.
  • "block" — renders
  • "circle" — renders
The caret is automatically hidden when the last block contains an incomplete code fence or a table, where a trailing character would disrupt the layout.
className
string
Additional CSS class name(s) applied to the outer <div> wrapper. Combined with Streamdown’s default layout classes (space-y-4 whitespace-normal).
plugins
PluginConfig
Optional plugin bundle providing syntax highlighting, math, diagram, and CJK support.
remend
RemendOptions
Options forwarded to the remend preprocessor, which repairs incomplete Markdown syntax during streaming. Only applies in mode="streaming" when parseIncompleteMarkdown is true.
controls
ControlsConfig
default:"true"
Configures which controls appear on interactive blocks (code blocks, tables, Mermaid diagrams). Pass true to enable all controls, false to disable all, or an object for granular control.See ControlsConfig for the full type.
shikiTheme
[ThemeInput, ThemeInput]
default:"[\"github-light\", \"github-dark\"]"
A tuple of [lightTheme, darkTheme] passed to Shiki for syntax highlighting. Accepts any BundledTheme string or a custom ThemeRegistrationAny object.
<Streamdown shikiTheme={["catppuccin-latte", "catppuccin-mocha"]}>
  {markdown}
</Streamdown>
When using the @streamdown/code plugin, its getThemes() result takes precedence over this prop.
mermaid
MermaidOptions
Options for Mermaid diagram rendering. See MermaidOptions.
Configuration for the link safety confirmation modal shown before navigating to external URLs. See LinkSafetyConfig.
lineNumbers
boolean
default:"true"
Show line numbers in code blocks.
allowedTags
AllowedTags
Allow custom HTML tags through the sanitization layer. Provide a map of tag names to their permitted attributes.
<Streamdown
  allowedTags={{ mention: ["user_id"], badge: ["type"] }}
>
  {`<mention user_id="123">@alice</mention>`}
</Streamdown>
The type is Record<string, string[]> where keys are tag names and values are arrays of allowed attribute names.
This only works with the default rehypePlugins. If you supply custom rehype plugins that replace the default sanitizer, you must handle custom tags within those plugins.
literalTagContent
string[]
Tags whose children should be treated as plain text instead of parsed Markdown. Useful for custom entity tags in AI UIs (e.g. <mention>) where child text contains Markdown metacharacters that should not be interpreted.The tag must also be present in allowedTags.
<Streamdown
  allowedTags={{ mention: ["user_id"] }}
  literalTagContent={["mention"]}
>
  {`<mention user_id="123">@_some_username_</mention>`}
</Streamdown>
translations
Partial<StreamdownTranslations>
Override UI strings for internationalisation or custom labels. Merged with the built-in defaultTranslations.
<Streamdown
  translations={{
    copyCode: "Copiar código",
    downloadCode: "Descargar código",
  }}
>
  {markdown}
</Streamdown>
icons
Partial<IconMap>
Override the default SVG icons used in controls (copy, download, fullscreen, etc.). Provide a partial map — only the icons you specify are replaced.
prefix
string
Tailwind CSS prefix to prepend to all internal utility classes. For example, prefix="tw" produces tw:flex instead of flex, enabling Tailwind v4’s prefix() support.
User-supplied className values are also prefixed when this option is set.
parseIncompleteMarkdown
boolean
default:"true"
When true (in mode="streaming"), runs the remend preprocessor over the incoming Markdown before rendering to repair incomplete syntax (unclosed bold, partial code fences, etc.).Set to false if your content is always complete or you want to handle preprocessing yourself.
normalizeHtmlIndentation
boolean
default:"false"
When true, strips 4+ leading spaces or tabs from HTML tags at the start of lines before parsing. This prevents Markdown parsers from treating deeply indented HTML as code blocks.Useful when rendering AI-generated HTML with nested tags indented for readability.
BlockComponent
React.ComponentType<BlockProps>
Replaces the built-in Block component used to render each parsed Markdown block. Use this to wrap blocks with custom logic, add per-block error boundaries, or instrument rendering.
import { Block, type BlockProps } from "streamdown";

function MyBlock(props: BlockProps) {
  return (
    <div className="my-block-wrapper">
      <Block {...props} />
    </div>
  );
}

<Streamdown BlockComponent={MyBlock}>{markdown}</Streamdown>
See Block for the full BlockProps reference.
parseMarkdownIntoBlocksFn
(markdown: string) => string[]
Replaces the default parseMarkdownIntoBlocks function used to split the Markdown string into discrete blocks. Each block is rendered by a separate BlockComponent instance, enabling fine-grained memoisation.The default implementation splits on double newlines while preserving code fences and other multi-line structures.
onAnimationStart
() => void
Called when isAnimating transitions from false to true. Also called on mount if isAnimating is initially true.Suppressed when mode="static".
onAnimationEnd
() => void
Called when isAnimating transitions from true to false.Suppressed when mode="static" and not called on the initial render.
components
Components
Custom React components to replace built-in HTML renderers. Based on the react-markdown Components type — any intrinsic HTML element tag can be overridden.An additional inlineCode key is supported to target inline <code> elements separately from block code.
<Streamdown
  components={{
    h1: ({ children }) => <h1 className="text-4xl">{children}</h1>,
    inlineCode: ({ children }) => <code className="bg-gray-100">{children}</code>,
  }}
>
  {markdown}
</Streamdown>
rehypePlugins
Pluggable[]
default:"defaultRehypePlugins"
Array of unified rehype plugins applied during HTML processing.Defaults to defaultRehypePlugins (rehype-raw, rehype-sanitize, rehype-harden). Replacing this array removes the default sanitization pipeline — ensure you add your own sanitization if you accept untrusted content.
Replacing the default rehype plugins disables allowedTags integration. If you need both custom plugins and custom tag allowlisting, extend the default sanitize schema manually.
remarkPlugins
Pluggable[]
default:"defaultRemarkPlugins"
Array of unified remark plugins applied during Markdown parsing.Defaults to defaultRemarkPlugins (remark-gfm, remarkCodeMeta). Plugin plugins from the plugins prop (math, CJK) are merged in automatically in the correct order.

Context

StreamdownContext

StreamdownContext is a React context exported from streamdown that child components — including custom components, BlockComponent, and plugin renderers — can read to access Streamdown’s resolved configuration.
import { StreamdownContext } from "streamdown";
import { useContext } from "react";

function MyCodeBlock() {
  const { isAnimating, lineNumbers, shikiTheme } = useContext(StreamdownContext);
  // ...
}
The context value is of type StreamdownContextType:
controls
ControlsConfig
The resolved controls configuration.
isAnimating
boolean
Whether the component is currently in an animating/streaming state.
lineNumbers
boolean
Whether line numbers are enabled for code blocks.
The resolved link safety configuration.
mermaid
MermaidOptions | undefined
The resolved Mermaid options.
mode
"static" | "streaming"
The current rendering mode.
shikiTheme
[ThemeInput, ThemeInput]
The active Shiki theme pair (light, dark). If a CodeHighlighterPlugin is provided, this reflects the plugin’s themes.

Default plugins

defaultRehypePlugins

An object (keyed by plugin name) containing the default rehype plugin pipeline:
export const defaultRehypePlugins: Record<string, Pluggable> = {
  raw: rehypeRaw,
  sanitize: [rehypeSanitize, defaultSanitizeSchema],
  harden: [
    harden,
    {
      allowedImagePrefixes: ["*"],
      allowedLinkPrefixes: ["*"],
      allowedProtocols: ["*"],
      defaultOrigin: undefined,
      allowDataImages: true,
    },
  ],
};
  • raw (rehype-raw) — Parses raw HTML nodes produced by remark-rehype back into a proper HAST tree.
  • sanitize (rehype-sanitize) — Strips disallowed HTML elements and attributes using a schema derived from rehype-sanitize’s defaultSchema, extended to allow tel: hrefs and metastring on <code> elements.
  • harden (rehype-harden) — Hardens URLs and origins for safe rendering.

defaultRemarkPlugins

An object (keyed by plugin name) containing the default remark plugin pipeline:
export const defaultRemarkPlugins: Record<string, Pluggable> = {
  gfm: [remarkGfm, {}],
  codeMeta: remarkCodeMeta,
};
  • gfm (remark-gfm) — Adds GitHub Flavored Markdown support: tables, task lists, strikethrough, autolinks.
  • codeMeta (remarkCodeMeta) — Exposes the raw metastring from code fences (everything after the language identifier) as a metastring prop on <code> elements.

Other exports

createAnimatePlugin

function createAnimatePlugin(options?: AnimateOptions): AnimatePlugin
Creates an AnimatePlugin instance for tracking per-block animation state. You only need this when building a custom BlockComponent — the Streamdown component creates and manages its own plugin internally when animated is set. The AnimatePlugin interface:
interface AnimatePlugin {
  name: "animate";
  type: "animate";
  rehypePlugin: Pluggable;
  setPrevContentLength: (length: number) => void;
  getLastRenderCharCount: () => number;
}

defaultTranslations

The default English strings for all translatable UI labels. Import and spread this to build partial translation overrides:
import { defaultTranslations } from "streamdown";

const translations = {
  ...defaultTranslations,
  copyCode: "Copiar código",
};

defaultUrlTransform

The default URL transform function from react-markdown. Passed as the urlTransform prop default. Import to extend or replace it:
import { defaultUrlTransform } from "streamdown";

Build docs developers (and LLMs) love