Monaco Editor Integration
Monaco integration is provided by stream-monaco and is optional. It supports fast, incremental updates for large code blocks.
Install:
pnpm add stream-monacoUse CodeBlockNode (default) to render Monaco-powered code blocks. For read-only usage, use MarkdownCodeBlockNode.
Tips:
- Defer Monaco initialization for offscreen code blocks
- Use
codeBlockStream: falseto avoid partial updates if desired - No additional CSS import is required
Vite & worker setup
Monaco requires worker packaging for production builds. Use vite-plugin-monaco-editor-esm to ensure workers are bundled into your app's build output. Example config:
import path from 'node:path'
import monacoEditorPlugin from 'vite-plugin-monaco-editor-esm'
export default defineConfig({
plugins: [
monacoEditorPlugin({
languageWorkers: [
'editorWorkerService',
'typescript',
'css',
'html',
'json',
],
customDistPath(root, buildOutDir, base) {
return path.resolve(buildOutDir, 'monacoeditorwork')
},
}),
],
})Preloading Monaco
To avoid a first-render flash when the first code block mounts, preload the Monaco integration during app initialization or on first route mount:
import { getUseMonaco } from 'markstream-vue'
getUseMonaco()getUseMonaco attempts to dynamically import stream-monaco and call its helper to register workers; if not available it fails gracefully and the code block falls back to a lightweight rendering.
Quick try — Render a monaco-enabled code block (after installing stream-monaco):
<script setup>
const node = { type: 'code_block', language: 'js', raw: 'console.log(123)', code: 'console.log(123)' }
</script>
<template>
<CodeBlockNode :node="node" />
</template>Add extra languages & themes
Only a minimal set of Monaco languages ships with the default integration to keep the first render fast. When you need additional grammars (Rust, Go, Bash, etc.) or want to ship your own VS Code themes, pass them through monacoOptions — either directly on CodeBlockNode or globally via MarkdownRender's codeBlockMonacoOptions prop. The object is forwarded to useMonaco() unchanged.
languagesis not appended to the built-in defaults fromstream-monaco; providing this array replaces the internaldefaultLanguages. Include every language you need (even the original ones) whenever you override it.
<script setup lang="ts">
import type { MonacoTheme } from 'stream-monaco'
import MarkdownRender from 'markstream-vue'
const docsDark: MonacoTheme = {
name: 'docs-dark',
base: 'vs-dark',
inherit: true,
colors: {
'editor.background': '#05060a',
},
rules: [],
}
const docsLight: MonacoTheme = {
name: 'docs-light',
base: 'vs',
inherit: true,
colors: {
'editor.background': '#ffffff',
},
rules: [],
}
const monacoOptions = {
languages: ['javascript', 'python', 'rust', 'shell'],
themes: [docsDark, docsLight],
theme: 'docs-dark',
MAX_HEIGHT: 640,
}
const markdown = `
\`\`\`python
print("extra languages go here")
\`\`\`
\`\`\`rust
fn main() {}
\`\`\`
`
</script>
<template>
<MarkdownRender
custom-id="docs"
:content="markdown"
:code-block-monaco-options="monacoOptions"
/>
</template>Each entry in
languagescan be a Monaco language id string or the loader signature thatstream-monacodocuments (for lazy language bundles). When not usingMarkdownRender, pass the samemonacoOptionsobject directly toCodeBlockNodevia:monaco-options.