DemoPreview
Embed runnable component demos in Markdown: the top half renders the component live, the bottom half shows its source. Three modes are supported — directory import, file import, and inline code — with the source panel inheriting the theme's code block styling (beam border, syntax highlighting, folding).
Features
- Three import modes — directory, file, inline code
- Source panel reuses theme styling — inherits beam border, syntax highlighting, folding
- Multi-file tabs — directory mode shows multiple files as tabs
- Style isolation — the preview area uses
not-proseto isolate demo components from doc styles - Zero config — auto-registered with the theme, just use
<DemoPreview>in Markdown
Directory Import
For multi-file demos. Specify a directory; index.vue runs as the demo, other files show as source tabs.
<DemoPreview dir="demos/alert" />diris relative to the project root (VitePress cwd)- Directory must contain
index.vue - Files sorted with
index.vuefirst, then alphabetically
File Import
Import a single .vue file as the demo.
<DemoPreview src="./MyButton.vue" />srcis relative to the current Markdown file
Inline Code
Write Vue code directly in Markdown, wrapped in a <DemoPreview> tag. The code is compiled via a Vite virtual module (no temp files on disk).
<DemoPreview>
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<button @click="count++">Clicked {{ count }} times</button>
</DemoPreview>Syntax convention
Code inside <DemoPreview> must follow these indentation rules:
- Top-level tags flush-left:
<script setup>,</script>, root elements (e.g.<button>) — no indentation - Inner code indented normally:
import,const, etc. follow standard Vue SFC conventions
Wrong (causes indentation issues):
<DemoPreview>
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<button @click="count++">{{ count }}</button>
</DemoPreview>Correct:
<DemoPreview>
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<button @click="count++">{{ count }}</button>
</DemoPreview>Live Example
A file import demo rendered live (inline code example on the test page):
How It Works
Three parts collaborate:
- Markdown plugin (
fxDemoPreviewPlugin) — recognizes the three syntaxes at compile time, rewrites them into<DemoPreview>component calls, auto-injects componentimports - Vite virtual module plugin (
fxDemoVirtualPlugin) — provides SFC content for inline code'simport 'virtual:fx-demo/<hash>.vue', compiled by vue (no disk temp files) - Preview container (
DemoPreview.vue) — top half renders the demo in<ClientOnly>+not-prose, bottom halfPreviewGroupshows source
Source display uses VitePress native code block rendering, so it inherits the theme's code block visuals (dark mode beam border, syntax highlighting, folding, etc.).
Notes
- Demo components run on the client (
ClientOnly); SSG shows a loading placeholder - The preview area uses
not-proseisolation — not affected byvp-docglobal styles - Inline
<script setup>is extracted to the virtual module's top level (Vue disallows script setup inside tags)
