TextPart in the React, Vue, and Solid UI packages now renders with TanStack Markdown. Pass extensions and highlighter. remarkPlugins, rehypePlugins, and disableDefaultPlugins are removed.
This page is only for apps that import the TextPart component. createChatHook does not use it unless you pass it as partsComponents.text.
| Old prop | New prop |
|---|---|
| remarkPlugins | extensions (MarkdownExtension[] from @tanstack/markdown) |
| rehypePlugins | extensions or highlighter |
| disableDefaultPlugins | Removed. The streaming extension is always on. |
| Solid components | Removed |
| React components | components (MarkdownComponents: a tag-name map with normal HTML props) |
A unified plugin, such as remark-gfm or remark-cjk-friendly, is not a TanStack Markdown extension. You cannot pass it through.
TanStack Markdown parses CJK bold with no plugin. Remove the plugin:
// Before
import { TextPart } from '@tanstack/ai-react/ui'
import remarkCjkFriendly from 'remark-cjk-friendly'
<TextPart content={content} remarkPlugins={[remarkCjkFriendly]} />// After
import { TextPart } from '@tanstack/ai-react/ui'
export function Reply({ content }: { content: string }) {
return <TextPart content={content} />
}The old stack ran rehype-highlight. Then rehype-sanitize removed the hljs-* classes, so fenced code already looked plain.
To get colors, pass a TanStack Highlight callback. For the full steps, see Highlight markdown code.
// Before
<TextPart content={content} />// After
import { TextPart } from '@tanstack/ai-react/ui'
import { highlightMarkdownCode } from './markdown-highlighter'
export function Reply({ content }: { content: string }) {
return (
<TextPart
content={content}
highlighter={highlightMarkdownCode}
className="markdown-renderer"
/>
)
}A unified plugin has no drop-in replacement. Write the behavior again as a TanStack Markdown extension, or do it outside TextPart.
You cannot turn off the streaming extension.
React still accepts components, but the type is now MarkdownComponents, not the Components type of react-markdown. TanStack Markdown passes only intrinsic HTML props.
A common override still type-checks, but renders wrong, because inline is always undefined:
// Before: compiles, but every code element renders as a block
<TextPart
content={content}
components={{
code: ({ inline, children }) =>
inline ? <code>{children}</code> : <pre>{children}</pre>,
}}
/>// After
import { TextPart } from '@tanstack/ai-react/ui'
export function Reply({ content }: { content: string }) {
return (
<TextPart
content={content}
components={{
a: (props) => <a {...props} target="_blank" rel="noreferrer" />,
}}
/>
)
}To tell a fence from inline code, read className (for example language-ts). Do not read inline or node.
These changes happen with a default <TextPart content={...} />. You get no type error.
| Input | Before | After |
|---|---|---|
| Bare URL https://example.com | Auto-linked | Plain text |
| Raw HTML such as <br> or <img> | Sanitized, then rendered | Escaped text |
| Task list | contains-task-list / task-list-item | Checkbox on a plain <li> |
| Fenced code | Plain (highlight classes removed) | Plain, with tm-code / language-* |
| CJK bold **…。**次 | Failed without a plugin | Works |
Tables, lists, strikethrough, and fenced code still render.
These things do not change:
Your type checker passes, and your replies render with TanStack Markdown. To add colored code fences, follow Highlight markdown code.