@assistant-ui/react-streamdown
v0.1.0
Published
Streamdown-based markdown rendering for assistant-ui
Readme
@assistant-ui/react-streamdown
Streamdown-based markdown rendering for assistant-ui. An alternative to @assistant-ui/react-markdown with built-in syntax highlighting, math, and diagram support.
When to Use
| Package | Best For |
|---------|----------|
| @assistant-ui/react-markdown | Lightweight, bring-your-own syntax highlighter |
| @assistant-ui/react-streamdown | Feature-rich with built-in Shiki, KaTeX, Mermaid |
Installation
npm install @assistant-ui/react-streamdown streamdownFor additional features, install the optional plugins:
npm install @streamdown/code @streamdown/math @streamdown/mermaid @streamdown/cjkUsage
Basic
import { StreamdownTextPrimitive } from "@assistant-ui/react-streamdown";
// Inside a MessagePartText component
<StreamdownTextPrimitive />With Plugins (Recommended)
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";
<StreamdownTextPrimitive
plugins={{ code, math, mermaid }}
shikiTheme={["github-light", "github-dark"]}
/>Migration from react-markdown
If you're migrating from @assistant-ui/react-markdown, you can use the compatibility API:
import { StreamdownTextPrimitive } from "@assistant-ui/react-streamdown";
// Your existing SyntaxHighlighter component still works
<StreamdownTextPrimitive
components={{
SyntaxHighlighter: MySyntaxHighlighter,
CodeHeader: MyCodeHeader,
}}
componentsByLanguage={{
mermaid: { SyntaxHighlighter: MermaidRenderer }
}}
/>Props
| Prop | Type | Description |
|------|------|-------------|
| mode | "streaming" \| "static" | Rendering mode. Default: "streaming" |
| plugins | PluginConfig | Streamdown plugins (code, math, mermaid, cjk) |
| shikiTheme | [string, string] | Light and dark theme for Shiki |
| components | object | Custom components including SyntaxHighlighter and CodeHeader |
| componentsByLanguage | object | Language-specific component overrides |
| preprocess | (text: string) => string | Text preprocessing function |
| controls | boolean \| object | Enable/disable UI controls for code blocks and tables |
| containerProps | object | Props for the container div |
| containerClassName | string | Class name for the container |
| remarkRehypeOptions | object | Options passed to remark-rehype during processing |
| BlockComponent | React.ComponentType | Custom component for rendering blocks |
| parseMarkdownIntoBlocksFn | (md: string) => string[] | Custom block parsing function |
| remend | object | Incomplete markdown auto-completion config |
| linkSafety | object | Link safety confirmation config |
| mermaid | object | Mermaid diagram rendering config |
| allowedTags | object | HTML tags whitelist |
Differences from react-markdown
- Streaming optimization: Uses block-based rendering and
remendfor better streaming performance - Built-in highlighting: Shiki is integrated via
@streamdown/codeplugin - No smooth prop: Streaming animation is handled by streamdown's
modeandisAnimating - Auto isAnimating: Automatically detects streaming state from message context
Advanced Usage
Custom Block Rendering
Use BlockComponent to customize how individual markdown blocks are rendered:
<StreamdownTextPrimitive
BlockComponent={({ content, index }) => (
<div key={index} className="my-block">
{/* Custom block rendering */}
</div>
)}
/>Custom Block Parsing
Override the default block splitting logic:
<StreamdownTextPrimitive
parseMarkdownIntoBlocksFn={(markdown) => markdown.split(/\n{2,}/)}
/>Using Hooks
import {
useIsStreamdownCodeBlock,
useStreamdownPreProps,
} from "@assistant-ui/react-streamdown";
// Inside a code component
function MyCodeComponent() {
const isCodeBlock = useIsStreamdownCodeBlock();
const preProps = useStreamdownPreProps();
if (!isCodeBlock) {
return <code className="inline-code">...</code>;
}
return <pre {...preProps}>...</pre>;
}Performance Best Practices
Use memoized components: Custom
SyntaxHighlighterandCodeHeadercomponents should be memoized to avoid unnecessary re-renders.Avoid inline function props: Define
preprocess,parseMarkdownIntoBlocksFn, and other callbacks outside the render function or wrap them inuseCallback.Plugin configuration: Pass plugin objects by reference (not inline) to prevent recreation on each render.
// Good
const plugins = useMemo(() => ({ code, math }), []);
<StreamdownTextPrimitive plugins={plugins} />
// Avoid
<StreamdownTextPrimitive plugins={{ code, math }} />License
MIT
