Streamdown splits the incoming Markdown string into discrete blocks using parseMarkdownIntoBlocks. Each block is rendered by a Block component instance. Block is exported so you can build a custom BlockComponent that wraps or extends its behavior.
How Block is used internally
Inmode="streaming", Streamdown calls parseMarkdownIntoBlocksFn to produce an array of Markdown strings (one per block). For each string, it renders:
BlockComponent is the built-in Block. Blocks are keyed by their index so React can reconcile updates without unmounting stable blocks when content grows.
In mode="static", Streamdown bypasses block splitting and renders the entire Markdown string in a single Markdown call. Block is not used.
BlockProps
Block accepts BlockProps, which extends the Options type from the internal Markdown renderer (equivalent to react-markdown’s options) with streaming-specific fields.
The raw Markdown string for this block. Streamdown passes one element from the
parseMarkdownIntoBlocksFn output. The block has already had remend and literal-tag preprocessing applied by the parent Streamdown component.Zero-based position of this block in the full block array. Used for stable keying and is accessible to custom
BlockComponent implementations.Block receives index but does not pass it to the DOM. It is stripped before rendering to avoid unknown-prop warnings.true when this block is the last block and isAnimating is true and the block contains an incomplete code fence. Components can read this value via the useIsCodeFenceIncomplete hook to conditionally suppress rendering features (e.g. copy buttons) until the fence is closed.Resolved text direction for this block. When
Streamdown is configured with dir="auto", each block’s direction is detected individually using the Unicode first-strong-character algorithm. For dir="ltr" or dir="rtl", all blocks receive the same value. When no dir prop is set on Streamdown, this is undefined and no dir attribute is written to the DOM.Block wraps the rendered Markdown in a <div dir={dir} style={{ display: "contents" }}> when a direction is present.Mirrors the parent
Streamdown’s parseIncompleteMarkdown prop. Block receives this for internal bookkeeping; the actual preprocessing is performed by Streamdown before blocks are created.This prop is not forwarded to the DOM or to the underlying
Markdown renderer.Mirrors the parent
Streamdown’s normalizeHtmlIndentation prop. When true, Block calls normalizeHtmlIndentation on content before passing it to the Markdown renderer, stripping 4+ leading spaces from HTML tag lines to prevent them from being interpreted as code blocks.An
AnimatePlugin instance created by createAnimatePlugin. When provided and isAnimating is true, Block reads the previous render’s character count from the plugin before rendering, ensuring only newly streamed characters receive entrance animations.Pass null or omit to disable animation tracking for this block.Custom React components for HTML elements, forwarded directly to the underlying Markdown renderer. Inherits from the merged
components computed by Streamdown.Rehype plugins forwarded to the Markdown renderer. Streamdown passes its merged pipeline (default plugins + any added by
plugins, allowedTags, literalTagContent, and the animate plugin).Remark plugins forwarded to the Markdown renderer. Streamdown passes its merged pipeline (default plugins + any added by
plugins.math and plugins.cjk).Memoization
Block is wrapped in React.memo with a custom comparison function. It skips re-renders unless one of the following changes:
contentshouldNormalizeHtmlIndentationindexisIncompletedircomponents(shallow key + value comparison)rehypePlugins(reference comparison)remarkPlugins(reference comparison)
When to use a custom BlockComponent
Use theBlockComponent prop on Streamdown to replace Block when you need to:
- Add a per-block error boundary
- Inject per-block wrapper elements (e.g. for layout or animation)
- Instrument rendering for analytics or debugging
- Apply custom memoization strategies
