A Slidev addon for rendering D2 diagrams in your presentations with full theme support, dark mode sync, and animations.
- Render D2 diagrams directly in Slidev presentations
- Automatic dark mode theme sync with Slidev
- Support for all D2 themes (20+ built-in themes)
- Sketch/hand-drawn mode
- Multi-board animations
- File imports (D2
@directive support) - Multiple layout engines (dagre, elk)
- Reactive updates when code changes
- TypeScript support
npm install slidev-addon-d2-diagramsAdd the addon to your slides.md frontmatter:
---
addons:
- slidev-addon-d2-diagrams
---Then use the D2Diagram component in your slides:
<D2Diagram code="A -> B: Hello World" />| Prop | Type | Description |
|---|---|---|
code |
string |
D2 diagram source code |
| Prop | Type | Default | Description |
|---|---|---|---|
themeId |
number |
- | Numeric theme ID (takes precedence over theme) |
theme |
string |
- | Named theme (see Available Themes) |
darkThemeId |
number |
200 |
Theme ID for dark mode |
darkTheme |
string |
- | Named theme for dark mode |
autoSyncDarkMode |
boolean |
true |
Sync theme with Slidev dark mode |
| Prop | Type | Default | Description |
|---|---|---|---|
sketch |
boolean |
false |
Enable sketch/hand-drawn style |
center |
boolean |
true |
Center the diagram |
scale |
number |
1 |
Scale factor for output |
pad |
number |
100 |
Padding around diagram (pixels) |
layoutEngine |
'dagre' | 'elk' |
'dagre' |
Layout algorithm |
| Prop | Type | Default | Description |
|---|---|---|---|
animateInterval |
number |
- | Milliseconds between boards (for multi-board diagrams) |
target |
string |
- | Specific board to render (e.g., 'layers.x.*') |
| Prop | Type | Default | Description |
|---|---|---|---|
fs |
Record<string, string> |
- | Virtual file system for D2 imports |
inputPath |
string |
'index.d2' |
Main input file path when using fs |
| Prop | Type | Default | Description |
|---|---|---|---|
forceAppendix |
boolean |
false |
Add appendix for tooltips/links |
| Prop | Type | Default | Description |
|---|---|---|---|
width |
string | number |
- | Fixed container width |
height |
string | number |
- | Fixed container height |
maxWidth |
string |
'100%' |
Maximum container width |
maxHeight |
string |
'500px' |
Maximum container height |
fit |
boolean |
false |
Scale diagram to fit within container bounds |
| Event | Payload | Description |
|---|---|---|
compiled |
string (SVG) |
Emitted when diagram is successfully compiled |
error |
string |
Emitted when compilation fails |
| Slot | Props | Description |
|---|---|---|
loading |
- | Custom loading state |
error |
{ error: string } |
Custom error state |
neutral-default(0)neutral-grey(1)flagship-terrastruct(3)cool-classics(4)mixed-berry-blue(5)grape-soda(6)aubergine(7)colorblind-clear(8)vanilla-nitro-cola(100)orange-creamsicle(101)shirley-temple(102)earth-tones(103)everglade-green(104)buttered-toast(105)terminal(300)terminal-grayscale(301)origami(302)
dark-mauve(200)dark-flagship-terrastruct(201)
<D2Diagram code="client -> server: Request" /><D2Diagram code="A -> B -> C" theme="grape-soda" dark-theme="dark-mauve" /><D2Diagram code="user -> app: Login" sketch /><D2Diagram
:code="`
layers: {
step1: { A }
step2: { A -> B }
step3: { A -> B -> C }
}
`"
target="*"
:animate-interval="1000"
/>D2 supports importing definitions from other files using the @ syntax (not @import):
<script setup>
// Inline virtual file
const sharedDefs = `classes: { box: { shape: rectangle } }`
</script>
<D2Diagram
code="...@shared
a: { class: box }"
:fs="{ 'shared.d2': sharedDefs }"
/>D2 Import Syntax:
...@filename- Spread import: inserts file contents into current scopea: @filename- Regular import: assigns imported content to variablea- Omit the
.d2extension in import statements (D2 adds it automatically) - The
fsprop keys should include the.d2extension
Using External .d2 Files in Slidev:
Since Slidev processes slides as virtual files, use a Vite alias to import .d2 files:
// vite.config.ts
import { defineConfig } from 'vite'
import { fileURLToPath, URL } from 'node:url'
export default defineConfig({
resolve: {
alias: {
'@diagrams': fileURLToPath(new URL('./', import.meta.url)),
},
},
})Then import in your slides:
<script setup>
import shared from '@diagrams/shared.d2?raw'
</script>
<D2Diagram
code="...@shared
myNode: { class: myClass }"
:fs="{ 'shared.d2': shared }"
/><D2Diagram code="A -> B">
<template #loading>
<span>Rendering diagram...</span>
</template>
<template #error="{ error }">
<span style="color: red">Failed: {{ error }}</span>
</template>
</D2Diagram><D2Diagram code="A -> B; B -> C; C -> A" layout-engine="elk" />Use the fit prop to scale diagrams to fit within constrained containers:
<D2Diagram code="A -> B -> C -> D -> E" max-height="200px" fit />Without fit, diagrams render at their natural size and may overflow. With fit, the diagram scales down to fit within the maxHeight (or maxWidth) while preserving aspect ratio.
By default, the component automatically syncs with Slidev's dark mode. When Slidev is in dark mode, the diagram will use the darkTheme or darkThemeId prop (defaulting to dark-mauve).
To disable auto-sync:
<D2Diagram code="A -> B" :auto-sync-dark-mode="false" theme="terminal" /># Install dependencies
npm install
# Start development server
npm run dev
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Lint
npm run lint
# Format
npm run format:fixMIT