Renderer & Node Components
Use this page when you already know you need a specific exported component and want the shortest path to its props, events, CSS requirements, and the page that goes deeper.
If you are still deciding where to customize the pipeline, start with:
- Need parser helpers, AST hooks, or
setCustomComponents()strategy: API Reference - Need a scoped renderer replacement: Override Built-in Components
- Need trusted custom tags such as
thinking: Custom Tags & Advanced Components - Need configuration, performance, or toolbar tuning: Props & Options
Quick reference
| Component | Best for | Key props/events | Extra CSS / peers | Troubleshooting hooks |
|---|---|---|---|---|
MarkdownRender | Rendering full AST trees (default export) | Props: content / nodes, custom-id, final, parse-options, custom-html-tags, is-dark, code-block-props, mermaid-props, d2-props, infographic-props; events: copy, handleArtifactClick, click, mouseover, mouseout | Import markstream-vue/index.css inside a reset-aware layer (CSS is scoped under an internal .markstream-vue container) | Use setCustomComponents(customId, mapping) + custom-id to scope overrides; see CSS checklist |
CodeBlockNode | Monaco-powered code blocks, streaming diffs | node, monacoOptions, stream, loading; events: copy, previewCode; slots header-left / header-right; diff hover actions live under monacoOptions (diffHunkActionsOnHover, diffHunkHoverHideDelayMs, onDiffHunkAction) | Install stream-monaco (peer) + bundle Monaco workers | Blank editor => check worker bundling + SSR guards |
MarkdownCodeBlockNode | Lightweight highlighting via shiki | node, stream, loading; slots header-left / header-right | Requires shiki + stream-markdown | Use for SSR-friendly or low-bundle scenarios |
MermaidBlockNode | Progressive Mermaid diagrams | node, isDark, isStrict, maxHeight; emits copy, export, openModal, toggleMode | Peer mermaid >= 11; no extra CSS required | For async errors see /guide/mermaid |
D2BlockNode | Progressive D2 diagrams | node, isDark, maxHeight, progressiveRender, progressiveIntervalMs; toolbar toggles | Peer @terrastruct/d2; no extra CSS | Missing peer falls back to source; see /guide/d2 |
MathBlockNode / MathInlineNode | KaTeX rendering | node | Install katex and import katex/dist/katex.min.css | SSR requires client-only in Nuxt |
ImageNode | Custom previews/lightboxes | Props: fallback-src, show-caption, lazy, svg-min-height, use-placeholder; emits click, load, error | None, but respects global CSS | Wrap in a custom component + setCustomComponents to intercept events |
LinkNode | Animated underline, tooltips | color, underlineHeight, showTooltip | No extra CSS | Browser defaults can override a styles; import reset |
VmrContainerNode | Custom ::: containers | node (name, attrs, loading, children) | Minimal base CSS; override via setCustomComponents | JSON attrs are normalized onto node.attrs (keys without data-); invalid/partial JSON becomes attrs.attrs; args after name stored in attrs.args |
TypeScript exports
markstream-vue exports renderer and component prop interfaces:
import type {
CodeBlockNodeProps,
D2BlockNodeProps,
InfographicBlockNodeProps,
MermaidBlockNodeProps,
NodeRendererProps,
PreCodeNodeProps,
} from 'markstream-vue'
import type { CodeBlockNode } from 'stream-markdown-parser'Notes:
NodeRendererPropsmatches<MarkdownRender>props.CodeBlockNodeProps,MermaidBlockNodeProps,D2BlockNodeProps,InfographicBlockNodeProps, andPreCodeNodePropsall useCodeBlockNodefornode(uselanguage: 'mermaid'/language: 'd2'/language: 'd2lang'/language: 'infographic'to route specialized renderers).
Pick the right component quickly
- Use
MarkdownRenderfor almost every app-level integration. - Use
CodeBlockNode,MermaidBlockNode,D2BlockNode, orMathBlockNodewhen you are composing lower-level node renderers yourself. - Use
ImageNode,LinkNode, orVmrContainerNodewhen you only need to replace one built-in node with custom behavior. - If you render node components directly, wrap them in
<div class="markstream-vue">...</div>so packaged CSS variables and component styles apply.
MarkdownRender
Main entry point that takes Markdown AST content (string or parsed structure) and renders with built-in node components.
Quick reference
- Best for: full markdown documents in Vite, Nuxt, VitePress.
- Key props:
content/nodes,custom-id,final,parse-options,custom-html-tags - CSS order: import reset first, then add
markstream-vue/index.cssinside@layer components.
CSS scope
markstream-vue scopes its packaged CSS under an internal .markstream-vue container to reduce global style conflicts.
- When you use
MarkdownRender, this container is already present. - When you render node components directly, wrap them with
<div class="markstream-vue">...</div>so theme variables and component styles apply.
Usage ladder
<script setup lang="ts">
import MarkdownRender from 'markstream-vue'
const md = '# Hello\n\nUse custom-id to scope styles.'
</script>
<template>
<MarkdownRender custom-id="docs" :content="md" />
</template>// Register custom nodes
import { setCustomComponents } from 'markstream-vue'
import CustomImageNode from './CustomImageNode.vue'
setCustomComponents('docs', {
image: CustomImageNode,
})/* styles/main.css */
@import 'modern-css-reset';
@tailwind base;
@layer components {
@import 'markstream-vue/index.css';
}
[data-custom-id='docs'] .prose {
max-width: 720px;
}Performance props
- Batch rendering:
batchRendering,initialRenderBatchSize,renderBatchSize,renderBatchDelay, andrenderBatchBudgetMscontrol how many nodes switch from skeletons to real components per frame. This only activates when virtualization is disabled (:max-live-nodes="0"). - Deferred heavy nodes:
deferNodesUntilVisibleandviewportPriorityare enabled by default so Mermaid, D2, Monaco, and KaTeX only load near the viewport. - Virtualization window:
maxLiveNodeslimits how many rendered nodes stay in the DOM, andliveNodeBuffercontrols overscan. See Performance. - Code block fallback: use
renderCodeBlocksAsPreandcodeBlockStreamto downgrade standard code fences to<pre><code>or disable streaming updates during heavy workloads.
Common issues
- Broken styles: start with the CSS checklist.
- Utility class leakage: pass
custom-idand scope overrides with[data-custom-id="docs"]. - SSR errors: wrap browser-only peers such as Mermaid, D2, and Monaco with client-only guards.
Use this when
- You want the default parser + renderer path with the fewest moving parts.
- You need one integration point for streaming, virtualization, batching, custom tags, and scoped overrides.
- You want to choose between
contentandnodeswithout wiring individual node renderers yourself.
Most common combinations
content+custom-id: static or lightly customized documents.nodes+final: streaming or server-preparsed workflows.custom-html-tags+setCustomComponents(customId, mapping): trusted custom tags such asthinking.renderCodeBlocksAsPre: downgrade heavy code blocks during SSR or constrained environments.
CodeBlockNode
Monaco-backed code block renderer with streaming diff support and interactive toolbar actions.
- Best for: rich code review, diff inspection, live patches, hover actions.
- Key props:
node,monacoOptions,stream,loading - Events:
copy,previewCode - Slots:
header-left,header-right - Peers:
stream-monaco, plus Monaco worker setup in your bundler - Common gotcha: if the editor area is blank, verify worker bundling and SSR guards first
Reach for this when the code block itself is part of the product experience. If you only need syntax highlighting, prefer MarkdownCodeBlockNode.
Deep dive: CodeBlockNode, Monaco
MarkdownCodeBlockNode
Lightweight syntax-highlighted code block renderer powered by
shikiandstream-markdown.
- Best for: SSR-friendly docs, blog-style pages, smaller bundles
- Key props:
node,stream,loading - Slots:
header-left,header-right - Peers:
shiki,stream-markdown - Common gotcha: if highlighting never appears, confirm both peers are installed and loaded in the environment where rendering happens
Choose this when you do not need Monaco's editing surface or diff interactions.
MermaidBlockNode
Progressive Mermaid renderer with copy/export/modal hooks.
- Best for: large Mermaid graphs, AI-generated diagrams, user-triggered export
- Key props:
node,isDark,isStrict,maxHeight - Events:
copy,export,openModal,toggleMode - Peer:
mermaid>= 11 - Common gotcha: SSR setups must gate Mermaid initialization to the client
Deep dive: Mermaid, MermaidBlockNode
D2BlockNode
Progressive D2 diagram renderer for structured architecture diagrams and sequence-like layouts.
- Best for: D2-driven docs, generated architecture views, progressive rendering
- Key props:
node,isDark,maxHeight,progressiveRender,progressiveIntervalMs - Peer:
@terrastruct/d2 - Common gotcha: missing peer falls back to source display instead of the rendered diagram
Deep dive: D2
MathBlockNode / MathInlineNode
KaTeX-backed block and inline math renderers.
- Best for: technical docs, formulas, AI math responses
- Key prop:
node - Peer:
katex - Required CSS:
katex/dist/katex.min.css - Common gotcha: invisible math usually means KaTeX CSS is missing, not that parsing failed
ImageNode
Built-in image renderer with sensible defaults for captioning, fallbacks, and lazy behavior.
- Best for: custom previews, analytics hooks, lightbox integration
- Key props:
fallback-src,show-caption,lazy,svg-min-height,use-placeholder - Events:
click,load,error - Common pattern: wrap this with a custom component and register it through
setCustomComponents(customId, { image: CustomImageNode })
Deep dive: ImageNode
LinkNode
Link renderer with underline animation and optional tooltip behavior.
- Best for: docs sites and chat surfaces where link affordance needs to match your design system
- Key props:
color,underlineHeight,showTooltip - Common gotcha: browser defaults or resets can override anchor styles, so verify CSS order before assuming the component is broken
VmrContainerNode
Renderer for
:::containers and other normalized block containers coming from the parser.
- Best for: callouts, notices, AI-specific block types, container-style custom components
- Key prop:
nodewithname,attrs,loading, andchildren - Normalization detail: JSON attrs are normalized onto
node.attrs; invalid or partial JSON is preserved underattrs.attrs; args after the container name are stored inattrs.args - Common pattern: use this together with
custom-html-tagsor parser hooks when you need trusted structured blocks
Direct node rendering checklist
When you bypass MarkdownRender and mount node components directly:
- Wrap them in a
.markstream-vuecontainer. - Import the same CSS you would for the full renderer.
- Install and guard only the peers needed by the nodes you actually render.
- Scope any replacement styles with a parent selector or
data-custom-idequivalent.
Need a different layer of the stack?
- Use API Reference for parser helpers,
setCustomComponents, and AST hooks. - Use Props & Options for full renderer prop behavior.
- Use Troubleshooting if the right component is already chosen but CSS, peers, or SSR still misbehave.