Fast, streaming-friendly Markdown rendering for Vue 3 β progressive Mermaid, streaming diff code blocks, and real-time previews optimized for large documents.
- TL;DR Highlights
- Try It Now
- Quick Start
- Common commands
- Streaming in 30 seconds
- Performance presets
- Key props & options
- Where it shines
- FAQ
- Why markstream-vue
- Releases
- Showcase
- Contributing & community
- Community & support
- Troubleshooting
π All detailed documentation, API, examples, and advanced usage have been migrated to the VitePress documentation site: https://markstream-vue-docs.simonhe.me/guide/
- Purpose-built for streaming Markdown (AI/chat/SSE) with zero flicker and predictable memory.
- Two render modes: virtual window for long docs, incremental batching for βtypingβ effects.
- Progressive diagrams (Mermaid) and streaming code blocks (Monaco/Shiki) that keep up with diffs.
- Works with raw Markdown strings or pre-parsed nodes, supports custom Vue components inline.
- TypeScript-first, ship-ready defaults β import CSS and render.
- Playground (interactive demo): https://markstream-vue.simonhe.me/
- Interactive test page (shareable links, easy reproduction): https://markstream-vue.simonhe.me/test
- Docs: https://markstream-vue-docs.simonhe.me/guide/
- One-click StackBlitz demo: https://stackblitz.com/github/Simon-He95/markstream-vue?file=playground/src/App.vue
- Changelog: CHANGELOG.md
- Nuxt playground:
pnpm play:nuxt - Discord: https://discord.gg/vkzdkjeRCW
- Discussions: https://github.com/Simon-He95/markstream-vue/discussions
- Discord: https://discord.gg/vkzdkjeRCW
- Issues: please use templates and attach a repro link (https://markstream-vue.simonhe.me/test)
The test page gives you an editor + live preview plus βgenerate share linkβ that encodes the input in the URL (with a fallback to open directly or pre-fill a GitHub Issue for long payloads).
pnpm add markstream-vue
# npm install markstream-vue
# yarn add markstream-vueimport MarkdownRender from 'markstream-vue'
// main.ts
import { createApp } from 'vue'
import 'markstream-vue/index.css'
createApp({
components: { MarkdownRender },
template: '<MarkdownRender custom-id="docs" :content="doc" />',
setup() {
const doc = '# Hello from markstream-vue\\n\\nSupports **streaming** nodes.'
return { doc }
},
}).mount('#app')Import markstream-vue/index.css after your reset (e.g., Tailwind @layer components) so renderer styles win over utility classes. Install optional peers such as stream-monaco, shiki, mermaid, and katex only when you need Monaco code blocks, Shiki highlighting, diagrams, or math.
Enable heavy peers only when needed:
import { enableKatex, enableMermaid } from 'markstream-vue'
import 'markstream-vue/index.css'
// after you install `mermaid` / `katex` peers
enableMermaid()
enableKatex()// plugins/markstream-vue.client.ts
import { defineNuxtPlugin } from '#app'
import MarkdownRender from 'markstream-vue'
import 'markstream-vue/index.css'
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.component('MarkdownRender', MarkdownRender)
})Then use <MarkdownRender :content="md" /> in your pages.
pnpm devβ playground dev serverpnpm play:nuxtβ Nuxt playground devpnpm buildβ library + CSS buildpnpm testβ Vitest suite (pnpm test:updatefor snapshots)pnpm typecheck/pnpm lintβ type and lint checks
Render streamed Markdown (SSE/websocket) with incremental updates:
import type { ParsedNode } from 'markstream-vue'
import MarkdownRender, {
getMarkdown,
parseMarkdownToStructure
} from 'markstream-vue'
import { ref } from 'vue'
const nodes = ref<ParsedNode[]>([])
const buffer = ref('')
const md = getMarkdown()
function addChunk(chunk: string) {
buffer.value += chunk
nodes.value = parseMarkdownToStructure(buffer.value, md)
}
// e.g., inside your SSE/onmessage handler
eventSource.onmessage = event => addChunk(event.data)
// template
// <MarkdownRender
// :nodes="nodes"
// :max-live-nodes="0"
// :batch-rendering="{
// renderBatchSize: 16,
// renderBatchDelay: 8,
// }"
// />Switch rendering style per surface:
- Virtualized window (default): steady scrolling and memory usage for long docs.
- Incremental batches: set
:max-live-nodes="0"for AI-like βtypingβ with lightweight placeholders.
Pre-parse Markdown on the server or in a worker and render typed nodes on the client:
// server or worker
import { getMarkdown, parseMarkdownToStructure } from 'markstream-vue'
const md = getMarkdown()
const nodes = parseMarkdownToStructure('# Hello\n\nThis is parsed once', md)
// send `nodes` JSON to the client<!-- client -->
<MarkdownRender :nodes="nodesFromServer" />This avoids client-side parsing and keeps SSR/hydration deterministic.
- Server: parse the first Markdown batch to nodes and serialize
initialNodes(and the rawinitialMarkdownif you also stream later chunks). - Client: hydrate with the same parser options, then keep streaming:
import type { ParsedNode } from 'markstream-vue'
import { getMarkdown, parseMarkdownToStructure } from 'markstream-vue'
import { ref } from 'vue'
const nodes = ref<ParsedNode[]>(initialNodes)
const buffer = ref(initialMarkdown)
const md = getMarkdown() // match server setup
function addChunk(chunk: string) {
buffer.value += chunk
nodes.value = parseMarkdownToStructure(buffer.value, md)
}This avoids re-parsing SSR content while letting later SSE/WebSocket chunks continue the stream.
- Virtual window (default) β keep
max-live-nodesat its default320to enable virtualization. Nodes render immediately and the renderer keeps a sliding window of elements mounted so long docs remain responsive without showing skeleton placeholders. - Incremental stream β set
:max-live-nodes="0"when you want a true typewriter effect. This disables virtualization and turns on incremental batching governed bybatchRendering,initialRenderBatchSize,renderBatchSize,renderBatchDelay, andrenderBatchBudgetMs, so new content flows in small slices with lightweight placeholders.
Pick one mode per surface: virtualization for best scrollback and steady memory usage, or incremental batching for AI-style βtypingβ previews.
Tip: In chats, combine
max-live-nodes="0"with smallrenderBatchSize(e.g.,16) and a tinyrenderBatchDelay(e.g.,8ms) to keep the βtypingβ feel smooth without jumping large chunks. TunerenderBatchBudgetMsdown if you need to cap CPU per frame.
contentvsnodes: pass raw Markdown or pre-parsed nodes (fromparseMarkdownToStructure).max-live-nodes:320(default virtualization) or0(incremental batches).batchRendering: fine-tune batches withinitialRenderBatchSize,renderBatchSize,renderBatchDelay,renderBatchBudgetMs.enableMermaid/enableKatex/enableMonaco: opt-in heavy deps when needed.parse-options: reuse parser hooks (e.g.,preTransformTokens,requireClosingStrong) on the component.custom-components: register inline Vue components for custom tags/markers.
Example: map Markdown placeholders to Vue components
import { setCustomComponents } from 'markstream-vue'
setCustomComponents({
CALLOUT: () => import('./components/Callout.vue'),
})
// Markdown: [[CALLOUT:warning title="Heads up" body="Details here"]]Or pass per-renderer:
<MarkdownRender
:content="doc"
:custom-components="{
CALLOUT: () => import('./components/Callout.vue'),
}"
/>Parse hooks example (match server + client):
<MarkdownRender
:content="doc"
:parse-options="{
requireClosingStrong: true,
preTransformTokens: (tokens) => tokens,
}"
/>- AI/chat UIs with long-form answers and Markdown tokens arriving over SSE/websocket.
- Docs, changelogs, and knowledge bases that need instant load but stay responsive as they grow.
- Streaming diffs and code review panes that benefit from Monaco live updates.
- Diagram-heavy content that should render progressively (Mermaid) without blocking.
- Embedding Vue components in Markdown-driven surfaces (callouts, widgets, CTA buttons).
- Mermaid/KaTeX not rendering? Install the peer (
mermaid/katex) and pass:enable-mermaid="true"/:enable-katex="true"or call the loader setters. - Bundle size: peers are optional and not bundled; import only
markstream-vue/index.cssonce. Use Shiki (MarkdownCodeBlockNode) when Monaco is too heavy. - Custom UI: register components via
setCustomComponents(global) orcustom-componentsprop, then emit markers/placeholders in Markdown and map them to Vue components.
| Needs | Typical Markdown preview | markstream-vue |
|---|---|---|
| Streaming input | Re-renders whole tree, flashes | Incremental batches with virtual windowing |
| Large code blocks | Slow re-highlight | Monaco streaming updates + Shiki option |
| Diagrams | Blocks while parsing | Progressive Mermaid with graceful fallback |
| Custom UI | Limited slots | Inline Vue components & typed nodes |
| Long docs | Memory spikes | Configurable live-node cap for steady usage |
- More βinstant startβ templates (Vite + Nuxt + Tailwind) and updated StackBlitz.
- Additional codeblock presets (diff-friendly Shiki themes, Monaco decoration helpers).
- Cookbook docs for AI/chat patterns (SSE/WebSocket, retry/resume, markdown mid-states).
- More showcase examples for embedding Vue components inside Markdown surfaces.
- Latest: Releases β see highlights and upgrade notes.
- Full history: CHANGELOG.md
- Recent highlights (0.0.3-beta.1/beta.0):
- Parser bumped to
stream-markdown-parser@0.0.36for parsing fixes. - Monaco upgrades with more languages/themes + diff-friendly code block tweaks.
- HTML/SVG preview dialog and AST debug view in the playground.
- Parser bumped to
Build something with markstream-vue? Open a PR to add it here (include a link + 1 screenshot/GIF). Ideal fits: AI/chat UIs, streaming docs, diff/code-review panes, or Markdown-driven pages with embedded Vue components.
- FlowNote β streaming Markdown note app demo (SSE + virtual window) β https://markstream-vue.simonhe.me/
- AI Chat surface β playground βtestβ page showing incremental batches + share links β https://markstream-vue.simonhe.me/test
A short video introduces the key features and usage of markstream-vue:
Watch on Bilibili: Open in Bilibili
- β‘ Extreme performance: minimal re-rendering and efficient DOM updates for streaming scenarios
- π Streaming-first: native support for incomplete or frequently updated tokenized Markdown
- π§ Monaco streaming updates: high-performance Monaco integration for smooth incremental updates of large code blocks
- πͺ Progressive Mermaid: charts render instantly when syntax is available, and improve with later updates
- π§© Custom components: embed custom Vue components in Markdown content
- π Full Markdown support: tables, formulas, emoji, checkboxes, code blocks, etc.
- π Real-time updates: supports incremental content without breaking formatting
- π¦ TypeScript-first: complete type definitions and IntelliSense
- π Zero config: works out of the box in Vue 3 projects
- π¨ Flexible code block rendering: choose Monaco editor (
CodeBlockNode) or lightweight Shiki highlighting (MarkdownCodeBlockNode) - π§° Parser toolkit:
stream-markdown-parsernow documents how to reuse the parser in workers/SSE streams and feed<MarkdownRender :nodes>directly, plus APIs for registering global plugins and custom math helpers.
- Read the contributor guide: CONTRIBUTING.md and follow the PR template.
- Be kind and follow the Code of Conduct.
- Issues: use templates for bugs/requests; attach a repro from https://markstream-vue.simonhe.me/test when possible.
- Questions? Start a discussion: https://github.com/Simon-He95/markstream-vue/discussions
- Chat live: Discord https://discord.gg/vkzdkjeRCW
- Looking to contribute? Start with good first issues.
- Support guide: SUPPORT.md
- PRs: follow Conventional Commits, add tests for parser/render changes, and include screenshots/GIFs for UI tweaks.
- If the project helps you, consider starring and sharing the repo β it keeps the work sustainable.
- Security: see SECURITY.md to report vulnerabilities privately.
- Add repro links/screenshots to existing issues.
- Improve docs/examples (especially streaming + SSR/worker patterns).
- Share playground/test links that showcase performance edge cases.
Troubleshooting has moved into the docs: https://markstream-vue-docs.simonhe.me/guide/troubleshooting
If you can't find a solution there, open a GitHub issue: https://github.com/Simon-He95/markstream-vue/issues
- Reproduce in the test page and click βgenerate share linkβ: https://markstream-vue.simonhe.me/test
- Open a bug report with the link and a screenshot: https://github.com/Simon-He95/markstream-vue/issues/new?template=bug_report.yml
This project uses and benefits from:
Thanks to the authors and contributors of these projects!
