markstream-svelte
v2.0.13
Published
Svelte 5 and SvelteKit streaming Markdown renderer for AI chat, LLM token streams, SSE/WebSocket output, incomplete Markdown, Mermaid, KaTeX, stream-diffs powered code blocks, and custom Svelte components.
Maintainers
Keywords
Readme
markstream-svelte
Svelte 5 streaming Markdown renderer for AI chat, LLM token streams, SSE/WebSocket output, incomplete Markdown states, long documents, custom components, Mermaid, KaTeX, stream-diffs code blocks, D2, and Infographic.
The coordinated 2.0 family beta will use markstream-svelte@next. Before installing, verify that npm view markstream-svelte@next version reports 0.1.0-beta.1. It removes the former Monaco code-block API in favor of stream-diffs; read the coordinated 2.0 family migration guide before upgrading.
pnpm add markstream-svelte@next svelte@^5 stream-diffsWhen to use it
Use markstream-svelte when Markdown changes while users are reading it:
LLM output, SSE streams, WebSocket streams, AI chat messages, long generated answers,
progressive diagrams, math, or code blocks.
For normal chat streaming, start with the raw content string path. Use pre-parsed
nodes only when another part of your app already owns the parser or AST state.
Known limitations
- Svelte 5 only. Svelte 4 is not supported.
- This package is currently beta. Check npm and the Svelte guide for the latest API maturity.
- It is not the first choice for short static Markdown or apps that require a fully stable Svelte 4-compatible API.
Install
pnpm add markstream-svelte svelte@^5Optional heavy renderers stay as peer dependencies, matching the Vue and React packages. Plain Markdown does not require these packages:
pnpm add katex mermaid stream-diffs @terrastruct/d2 @antv/infographicstream-diffs powers the enhanced code blocks (smaller runtime, no monaco-editor).
Enhanced Code Blocks
CodeBlockNode renders a single code block with the header, toolbar, and a stream-diffs File / FileDiff surface. Inside MarkdownRender, code blocks resolve to the same runtime automatically.
<script lang="ts">
import type { CodeBlockOptions } from 'markstream-svelte'
import { CodeBlockNode } from 'markstream-svelte'
const node = {
type: 'code_block',
language: 'ts',
code: 'const answer = 42',
raw: 'const answer = 42',
}
const codeBlockOptions: CodeBlockOptions = {
overflow: 'wrap',
diffStyle: 'unified',
enableLineSelection: true,
}
</script>
<CodeBlockNode {node} {codeBlockOptions} isDark showHeader />Direct CodeBlockNode and top-level MarkdownRender both accept codeBlockOptions. It covers host-managed typography/layout and supported File/FileDiff, interaction, annotation, and callback fields; maxHeight and the single symmetric padding value use numeric CSS pixels. Use codeBlockProps for header/toolbar controls. Theme values are registered names and themes is the [dark, light] pair. An old Monaco JSON theme is not accepted directly: first convert it to a Shiki ThemeRegistration, then call registerCustomTheme(name, loader) from stream-diffs/pierre; see the coordinated 2.0 family migration guide. When stream-diffs is not installed, the block renders as a plain <pre>.
Basic Usage
<script lang="ts">
import MarkdownRender from 'markstream-svelte'
import 'markstream-svelte/index.css'
const content = `# Hello
Inline math: $E = mc^2$
\`\`\`mermaid
flowchart LR
A --> B
\`\`\`
`
</script>
<MarkdownRender {content} />Workers
<script lang="ts">
import MarkdownRender, { setKaTeXWorker, setMermaidWorker } from 'markstream-svelte'
import KatexWorker from 'markstream-svelte/workers/katexRenderer.worker?worker&inline'
import MermaidWorker from 'markstream-svelte/workers/mermaidParser.worker?worker&inline'
setKaTeXWorker(new KatexWorker())
setMermaidWorker(new MermaidWorker())
</script>
<MarkdownRender content="Inline math: $x^2$" />Custom Components
Register Svelte 5 components with the scoped registry:
<script lang="ts">
import MarkdownRender, { setCustomComponents } from 'markstream-svelte'
import ThinkingNode from './ThinkingNode.svelte'
const customId = 'demo'
setCustomComponents(customId, {
thinking: ThinkingNode,
})
</script>
<MarkdownRender
content="<thinking>nested markdown</thinking>"
{customId}
customHtmlTags={['thinking']}
/><!-- ThinkingNode.svelte -->
<script lang="ts">
import MarkdownRender from 'markstream-svelte'
let {
node,
customId = undefined,
}: {
node: any
customId?: string
} = $props()
</script>
<section class="thinking-node">
<MarkdownRender
content={String(node?.content ?? '')}
{customId}
customHtmlTags={['thinking']}
/>
</section>Run the local playground with:
pnpm play:svelte