# markstream-vue: a streaming Markdown renderer for Vue 3 and Nuxt AI chat

> markstream-vue renders Markdown that is still being written, rather than finished documents. It is the Vue member of a multi-framework renderer family, and the interesting question is whether a partial-parse renderer earns its place next to marked or markdown-it.

**Simon-He95/markstream-vue** — Multi-framework streaming Markdown renderers for AI apps: Vue/Nuxt, React/Next.js, Svelte, and Angular, with Mermaid, KaTeX, stream-diffs code blocks, safe HTML, and low-jitter updates.

- Repository: https://github.com/Simon-He95/markstream-vue
- Website: https://markstream.simonhe.me
- Stars: 3,024 · Forks: 186
- Language: Vue
- License: MIT
- Published: 2026-09-09 · Updated: 2026-09-09 · Language: en
- Canonical page: https://hysenlabs.com/projects/simon-he95-markstream-vue

## The problem markstream-vue solves: Markdown that is not finished yet

Most Markdown renderers assume they receive a complete document. marked, markdown-it and react-markdown all fit that assumption, and the README says so directly: use them for finished Markdown. An LLM chat stream breaks the assumption. Tokens arrive one at a time, so at any instant the buffer may contain an unclosed code fence, half a table row, or a Mermaid block with no closing delimiter. A renderer that reparses the full string on every chunk pays for the whole message again to display one new word, and the visible result is layout that jumps as delimiters open and close.

markstream-vue is built for that intermediate state. The README frames the split as a decision rule: use Markstream when the Markdown is still changing while the user is reading it. The audience is therefore narrow and concrete: teams building Vue 3, Nuxt or VitePress chat interfaces on SSE or WebSocket token streams, including mobile WebView targets where a reflow costs more than it does on desktop.

The package also carries a safe HTML policy and progressive handling of heavy blocks, which matters because streamed content is often untrusted text from a model rather than author-written Markdown.

## How the streaming pipeline is split across packages

The repository is a pnpm workspace, and the layout shows the renderer is not one monolith. Two framework-agnostic packages sit underneath the Vue component: stream-markdown-parser for parsing and markstream-core for streaming controller utilities. The Vue package, markstream-vue, wraps those into a component surface.

That split explains the project's multi-framework claim. Sibling packages for React and Next.js, Svelte 5, Angular standalone, Vue 2.6 and 2.7, and Octane all sit on the same parser and core, so the streaming behaviour is defined once and the framework packages adapt it. The README lists the mapping explicitly: markstream-react for React, Next.js and Remix, markstream-svelte for Svelte 5, markstream-angular for Angular standalone, markstream-vue2 for Vue 2, and markstream-octane for Octane.

The public API described as stable includes MarkdownRender, streaming content rendering, pre-parsed node rendering, the safe HTML policy, and optional Mermaid and KaTeX. Pre-parsed node rendering is the part worth noting: it lets an application feed already-parsed nodes rather than raw text, which is the escape hatch when you want to control parsing yourself.

The repository also contains STREAMING-CPU-PERFORMANCE-INVESTIGATION.md and STREAMING-CPU-PERFORMANCE-HANDOFF.md at the top level, plus PERFORMANCE_ANALYSIS.md. Those filenames indicate that streaming CPU cost has been treated as an open engineering problem rather than a solved one.

## Installing markstream-vue and rendering a first stream

The README states that Markstream 2.x is stable on npm's latest tag. Installation is a single package add:

```bash
pnpm add markstream-vue
```

One optional peer exists. The README says to add the stream-diffs peer only when you need enhanced code and diff blocks, and that version 2 removed the Monaco and stream-markdown code-block runtimes. If your interface shows diffs inside streamed answers, that peer is the piece to check before you upgrade.

The README gives this component example, which imports the stylesheet, declares content and isDone props, and renders in chat mode with final bound to the done flag:

```vue
<script setup lang="ts">
import MarkdownRender from 'markstream-vue'
import 'markstream-vue/index.css'

defineProps<{
  content: string
  isDone: boolean
}>()
</script>

<template>
  <MarkdownRender mode="chat" :content="content" :final="isDone" />
</template>
```

What you should see: the component updating as content grows, with the final prop telling it the stream has ended so it can settle the last incomplete block. Two things in that snippet are easy to miss. The stylesheet import is required, and the package marks CSS as a side effect in its exports, which is why the import is not tree-shaken away. The mode and final props are the two knobs the README shows; the documentation site at markstream.simonhe.me is where the full prop surface lives.

Applications that must stay on 1.x across the cutover can pin the maintained line instead:

```bash
pnpm add markstream-vue@1
```

## The 2.0 cutover is the sharpest edge in this project

The most important limitation is not a rendering bug. It is the version boundary. Version 2 removes the Monaco and stream-markdown code-block runtimes, and the README points readers to a migration guide before upgrading. Any application that relied on those runtimes for code display will lose that surface on upgrade unless it installs the optional stream-diffs peer.

That is a deliberate trade: a smaller stable core in exchange for a migration step for existing users. It also means the 1.x line is not abandoned but preserved, available through markstream-vue@1 and the legacy npm tag, with 1.x prereleases on legacy-next. Two maintained lines in parallel is a real cost for a small project, and the README does not describe when the legacy line stops receiving fixes.

A second boundary is framework scope. Vue 2 users need markstream-vue2, not markstream-vue. Parser-only consumers who do not want a component should use stream-markdown-parser directly. Choosing the wrong package is the most likely first mistake.

Finally, the presence of performance investigation documents in the repository root suggests streaming CPU cost is an area the maintainers are still working through. The README does not publish throughput numbers, and none should be assumed.

## markstream-vue compared with marked, markdown-it and react-markdown

The README's own comparison is a usage rule rather than a benchmark table. marked, markdown-it and react-markdown are for finished Markdown documents. Markstream is for Markdown that is still changing while the user reads it. The documentation site carries longer comparisons against vue-stream-markdown, Streamdown, react-markdown, and marked and markdown-it.

The difference in approach is where the work happens. A conventional renderer takes a string and produces output, and the caller decides when to call it. A streaming renderer takes a string that is expected to be incomplete and holds the intermediate states stable. That distinction is why the package exposes a final flag: the renderer needs to know whether the text it is holding might still grow.

For a documentation site or a blog, that machinery is dead weight. For a chat interface, the alternative is usually to debounce updates or to render into a plain text block until the stream finishes, both of which trade away the live appearance that users expect. The honest framing is that markstream-vue is not better than marked or markdown-it. It is aimed at a state those libraries were not designed to receive.

## Maintenance, licence and upgrade cost

The last push to the repository was on 2026-09-09, and the repository is not archived. Releases on that date include markstream-vue@2.0.11, markstream-vue2@2.0.11 and markstream-svelte@2.0.11, while the root package.json carries version 2.0.12. The version numbers moving in step across framework packages is consistent with the shared parser and core described in the README.

The licence is MIT, stated in package.json and in the repository's license file. MIT is permissive and imposes no source-disclosure requirement on applications that depend on the package. That is a statement about the licence text, not legal advice; if your organisation has specific obligations, a lawyer should read the file.

Upgrade cost concentrates in two places. The 2.0 migration guide covers code-block runtime changes, and the optional stream-diffs peer is the dependency question to settle first. Teams on 1.x can defer by pinning markstream-vue@1, but that defers rather than avoids the work, and the README does not say how long the legacy line stays supported.

## Conclusion

Adopt markstream-vue if your Vue 3, Nuxt or VitePress interface prints tokens as they arrive and your current renderer flickers, drops half-finished code fences, or reparses the whole message on every chunk. Skip it if you only render stored Markdown, if you depend on the Monaco or stream-markdown code-block runtimes removed in 2.0, or if you are on Vue 2 and unwilling to move to the markstream-vue2 package. Before upgrading, read the 1.x to 2.0 migration guide and confirm whether you need the optional stream-diffs peer.

## FAQ

### What is markstream-vue used for?

It renders Markdown that is still arriving, such as LLM token streams over SSE or WebSocket, inside Vue 3, Nuxt or VitePress interfaces. The README positions it for incomplete Markdown, long AI responses, streaming code blocks, Mermaid and KaTeX.

### How do I install markstream-vue?

The README gives pnpm add markstream-vue, with Markstream 2.x stable on npm's latest tag. The optional stream-diffs peer is only needed for enhanced code and diff blocks.

### Does markstream-vue work with Nuxt and VitePress?

Yes. The README lists markstream-vue for Vue 3, Nuxt and VitePress, and the documentation site has separate landing pages for the Vue and Nuxt cases.

### Is markstream-vue a replacement for marked or markdown-it?

Not for finished documents. The README says to use marked, markdown-it or react-markdown for finished Markdown, and Markstream when the Markdown is still changing while the user is reading it.

## Sources

- [License: MIT](https://github.com/Simon-He95/markstream-vue/blob/main/LICENSE)
- [Project website](https://markstream.simonhe.me)
- [README](https://github.com/Simon-He95/markstream-vue/blob/main/README.md)
- [Releases](https://github.com/Simon-He95/markstream-vue/releases)
- [Simon-He95/markstream-vue on GitHub](https://github.com/Simon-He95/markstream-vue)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/simon-he95-markstream-vue
