Stream Markdown
A Markdown renderer built for streaming output.
Usage
Basic
While streaming, an unclosed tail fence defers rendering instead of flashing in half-written.
Loading demo...
Best Practices
- Keep
sanitizeon: model output is untrusted, and this is where it becomes DOM. - Set
streamingto false when generation ends, or the caret stays at the tail. - In custom renderers, show a loading state while
closedis false instead of parsing a partial block. - Key
renderersby the lowercase first word of the fence (```chart→chart). - On non-English surfaces, pass the code and diagram wording along with the image and table copy: the built-in blocks show it, and it defaults to English.
- Use
TxMarkdownViewfor static Markdown; it needs none of the block and caret machinery.
API Reference
Props
| Name | Type | Default | Description |
|---|---|---|---|
content | string | — | The Markdown source. Required. |
streaming | boolean | false | Output is still arriving; drives the caret, the reveal, and deferred tail-fence rendering. |
reveal | 'aurora' | 'hue' | 'blur' | 'languid' | 'none' | 'aurora' | How new text enters, with TxStreamText's presets; languid keeps its slow blur but not its rise. |
caret | boolean | true | Shows the Tuff caret at the write head while streaming; switched off, it goes at once. |
sanitize | boolean | true | Sanitizes HTML through DOMPurify. |
theme | 'light' | 'dark' | 'auto' | 'auto' | Color theme; auto follows the environment. |
renderers | Record<string, StreamMarkdownBlockRenderer> | — | Block renderers by fence language; each receives StreamMarkdownBlockContext as props. |
blockRemoteImages | boolean | true | Shows remote images (http(s)://, //) as a placeholder until the reader loads them. |
blockedImageText | string | 'Remote image blocked' | Title of the blocked-image placeholder. |
loadImageOnceText | string | 'Load this image' | Text of the button that loads only this image. |
allowSessionImagesText | string | 'Allow for this conversation' | Text of the button that allows every remote image for the conversation. |
copyTableText | string | 'Copy CSV' | Text of a table's copy-as-CSV button. |
copiedTableText | string | 'Copied' | Button text shown briefly after a successful copy. |
codePreviewText | string | 'Preview' | Text of a code block's button that shows HTML, SVG, or XML rendered. |
codeSourceText | string | 'Code' | Text of the same button while the preview shows, which returns to the source. |
codeFrameLabelFormatter | (lang: string) => string | (lang) => `${lang} preview` | Builds the preview frame's accessible name from the fence language. |
copyCodeText | string | 'Copy' | Text of a code block's copy button. |
copiedCodeText | string | 'Copied' | Copy button text shown briefly after a successful copy. |
diagramZoomLabel | string | 'Zoom diagram' | Accessible name of a rendered diagram, which opens the preview. |
diagramPreviewLabel | string | 'Diagram preview' | Accessible name of the fullscreen diagram preview. |
diagramCloseLabel | string | 'Close preview' | Accessible name of the preview's close button. |
diagramErrorText | string | 'Diagram failed to render — showing source.' | Shown above the source when a diagram fails to render. |
TxCodeBlock Props
The built-in renderer for every language without one of its own, also exported for showing code outside a document. It receives StreamMarkdownBlockContext as props; inside TxStreamMarkdown, its copy comes from the code* props and copyCodeText / copiedCodeText.
| Name | Type | Default | Description |
|---|---|---|---|
previewable | boolean | true | Offers a rendered view of a settled HTML, SVG, or XML fence. |
previewLabel | string | 'Preview' | Text of the button that shows the rendered view. |
codeLabel | string | 'Code' | Text of the same button while the preview shows. |
frameLabelFormatter | (lang: string) => string | (lang) => `${lang} preview` | Builds the preview frame's accessible name from the fence language. |
copyLabel | string | 'Copy' | Text of the copy button. |
copiedLabel | string | 'Copied' | Copy button text shown briefly after a successful copy. |
TxMermaidBlock Props
The built-in mermaid renderer, which also receives StreamMarkdownBlockContext as props; inside TxStreamMarkdown, its copy comes from the diagram* props and copyCodeText / copiedCodeText.
| Name | Type | Default | Description |
|---|---|---|---|
zoomLabel | string | 'Zoom diagram' | Accessible name of the rendered diagram, which opens the preview. |
previewLabel | string | 'Diagram preview' | Accessible name of the fullscreen preview. |
closeLabel | string | 'Close preview' | Accessible name of the preview's close button. |
errorText | string | 'Diagram failed to render — showing source.' | Shown above the source when the diagram fails to render. |
copyLabel | string | 'Copy' | Text of the copy button on the source shown after a failed render. |
copiedLabel | string | 'Copied' | Copy button text shown briefly after a successful copy. |
Overview
- The document renders block by block, so appended text never reflows the whole page.
- While streaming, the still-growing tail fence defers its final rendering until it closes; renderers get
closed: falseonly for that fence. - Unregistered languages use the built-ins:
TxMermaidBlockformermaid,TxCodeBlockfor the rest. - The caret is
TxStreamText's: right after the last character of a paragraph, heading, or quote, otherwise on its own line below. Under reduced motion nothing enters and the caret rests. - DOMPurify loads dynamically; with
sanitizeon, nothing renders until it loads, or at all if loading fails. Sanitizing is no substitute for a server-side trust boundary. - Image consent is module-level and shared by every instance; call the exported
resetRemoteImagePolicy()when the conversation changes. - Each instance owns its own
Marked(gfm,breaks); the GitHub Markdown sheet is scoped under:where(.tx-md), leaving the host's.markdown-bodyalone.