Streaming response
The model can send ordinary Markdown as it thinks.
- completed blocks stay stable
- unfinished markers stay out of the way
- React and HTML stay in sync
const text = responseSoFar
return render(text)Unsafe links stay text-only
Markdown with an exit strategy.
Parse documents or accumulated AI output into a plain, serializable tree. Inspect it, cache it, index it, or render it as HTML, React, or Octane—without carrying a content framework or incremental parser state through your app.
3.0 ThousandTotal Downloads845Weekly Downloads4GitHub Stars---
title: Ship the docs
---
# One source
Parse it **once**. Render it where the product needs it.
- cache the tree
- index the text
- choose a rendererParsing does not trap your content inside a renderer. Edit the source and inspect the real serializable tree, deterministic HTML, or React output below.
{
"type": "root",
"frontmatter": "title: Durable content",
"children": [
{
"type": "heading",
"depth": 1,
"id": "one-source-many-destinations",
"children": [
{
"type": "text",
"value": "One source, many destinations"
}
]
},
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "Parse the document "
},
{
"type": "strong",
"children": [
{
"type": "text",
"value": "once"
}
]
},
{
"type": "text",
"value": ", then keep the tree."
}
]
},
{
"type": "list",
"ordered": false,
"items": [
{
"type": "listItem",
"children": [
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "cache it at the edge"
}
]
}
]
},
{
"type": "listItem",
"children": [
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "index its text"
}
]
}
]
},
{
"type": "listItem",
"children": [
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "render it with React or HTML"
}
]
}
]
}
]
},
{
"type": "blockquote",
"children": [
{
"type": "paragraph",
"children": [
{
"type": "text",
"value": "The renderer can change. The document does not."
}
]
}
]
}
]
}Append each chunk and pass the complete string back through TanStack Markdown. The optional streaming profile reparses synchronously, so there is no incremental state to coordinate, recover, or discard.
import { streamingMarkdownExtension } from '@tanstack/markdown/extensions/streaming'
const extensions = [streamingMarkdownExtension()]
<Markdown extensions={extensions} frontmatter={false} headingIds={false}>
{responseSoFar}
</Markdown># Streaming response
The model can send **ordinary Markdown** as it thinks.
- completed blocks stay stable
- unfinished markers stay out of the way
- React and HTML stay in sync
```ts
const text = responseSoFar
return render(text)
```
[Unsafe links stay text-only](javascript:alert("nope"))The model can send ordinary Markdown as it thinks.
const text = responseSoFar
return render(text)Unsafe links stay text-only
Technical docs need a known vocabulary, not an open-ended compiler platform. New syntax has to justify its bytes, its ambiguity, and its long-term maintenance cost.
Current CommonMark example accounting. Full conformance is explicitly not the goal; the supported docs profile is.
Raw HTML starts escaped, executable URL schemes are stripped, and text, attributes, and code are encoded at render time. The parser is also tested against malformed and adversarial input as part of its normal release gates.
<script>alert("not today")</script>
[Run code](javascript:alert("nope"))
**Trusted Markdown still renders.**<p><script>alert("not today")</script></p>
<p>Run code</p>
<p><strong>Trusted Markdown still renders.</strong></p>Split entry points keep the parser, renderers, framework adapters, and docs extensions independent. Import the layer the page needs.
Gzip sizes from the project benchmark report. Package scopes and feature sets are not equivalent.
Code fences carry language and metadata. An explicit highlighter can render them later, so the core never silently imports a grammar engine. The same boundary works with your own highlighter or TanStack Highlight.
Tiny, synchronous highlighting for the code fences your document model already understands.
Syntax fixtures, renderer parity, deterministic corpus checks, size budgets, and resilience limits ship with the library—not as an afterthought.
Keep the source. Own the model.