Skip to content

A Vue 3 renderer specifically built for AI-powered streaming Markdown: Monaco incremental, Mermaid progressive, and KaTeX formula speed, with real-time updates and no jitter, ready to use out of the box.

License

Notifications You must be signed in to change notification settings

Simon-He95/markstream-vue

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

markstream-vue

Fast, streaming-friendly Markdown rendering for Vue 3 β€” progressive Mermaid, streaming diff code blocks, and real-time previews optimized for large documents.

NPM version δΈ­ζ–‡η‰ˆ Docs Playground Test page NPM downloads Bundle size Release Discussions Discord Support Security CI License

Contents

πŸ“– All detailed documentation, API, examples, and advanced usage have been migrated to the VitePress documentation site: https://markstream-vue-docs.simonhe.me/guide/

TL;DR Highlights

  • 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.

πŸš€ Try It Now

πŸ’¬ Community & support

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).

⚑ Quick Start

pnpm add markstream-vue
# npm install markstream-vue
# yarn add markstream-vue
import 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()

Nuxt quick drop-in

// 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.

πŸ› οΈ Common commands

  • pnpm dev β€” playground dev server
  • pnpm play:nuxt β€” Nuxt playground dev
  • pnpm build β€” library + CSS build
  • pnpm test β€” Vitest suite (pnpm test:update for snapshots)
  • pnpm typecheck / pnpm lint β€” type and lint checks

⏱️ Streaming in 30 seconds

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.

SSR / Worker usage (deterministic output)

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.

Hybrid: SSR + streaming handoff

  • Server: parse the first Markdown batch to nodes and serialize initialNodes (and the raw initialMarkdown if 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.

βš™οΈ Performance presets

  • Virtual window (default) – keep max-live-nodes at its default 320 to 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 by batchRendering, initialRenderBatchSize, renderBatchSize, renderBatchDelay, and renderBatchBudgetMs, 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 small renderBatchSize (e.g., 16) and a tiny renderBatchDelay (e.g., 8ms) to keep the β€œtyping” feel smooth without jumping large chunks. Tune renderBatchBudgetMs down if you need to cap CPU per frame.

🧰 Key props & options (cheatsheet)

  • content vs nodes: pass raw Markdown or pre-parsed nodes (from parseMarkdownToStructure).
  • max-live-nodes: 320 (default virtualization) or 0 (incremental batches).
  • batchRendering: fine-tune batches with initialRenderBatchSize, 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,
  }"
/>

πŸ”₯ Where it shines

  • 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).

❓ FAQ (quick answers)

  • 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.css once. Use Shiki (MarkdownCodeBlockNode) when Monaco is too heavy.
  • Custom UI: register components via setCustomComponents (global) or custom-components prop, then emit markers/placeholders in Markdown and map them to Vue components.

πŸ†š Why markstream-vue over a typical Markdown renderer?

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

πŸ—ΊοΈ Roadmap (snapshot)

  • 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.

πŸ“¦ Releases

  • 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.36 for parsing fixes.
    • Monaco upgrades with more languages/themes + diff-friendly code block tweaks.
    • HTML/SVG preview dialog and AST debug view in the playground.

🧭 Showcase & examples

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.

πŸ“Ί Introduction Video

A short video introduces the key features and usage of markstream-vue:

Watch on Bilibili

Watch on Bilibili: Open in Bilibili

Features

  • ⚑ 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-parser now 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.

πŸ™Œ Contributing & community

Quick ways to help

  • 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 & Common Issues

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

Report an issue quickly

  1. Reproduce in the test page and click β€œgenerate share link”: https://markstream-vue.simonhe.me/test
  2. Open a bug report with the link and a screenshot: https://github.com/Simon-He95/markstream-vue/issues/new?template=bug_report.yml

Thanks

This project uses and benefits from:

Thanks to the authors and contributors of these projects!

Star History

Star History Chart

License

MIT Β© Simon He

About

A Vue 3 renderer specifically built for AI-powered streaming Markdown: Monaco incremental, Mermaid progressive, and KaTeX formula speed, with real-time updates and no jitter, ready to use out of the box.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project

Packages

No packages published

Contributors 15