# Slidev Admin & Architecture Guide

This guide is the deep reference for the people who operate, extend, deploy, or
contribute to **Slidev** — DevOps engineers wiring it into CI/CD, theme and addon
authors, and developers working on the codebase itself. It documents the architecture,
the complete configuration surface, the rendering pipeline internals, extension points,
deployment, and security considerations.

For day-to-day authoring (writing slides, presenting, exporting), see the companion
**Slidev User Guide**.

---

## 1. Executive Summary

Slidev is a Markdown-driven, web-based presentation tool for developers. A presentation
is a single Markdown file that Slidev parses into slides and renders as a
[Vue 3](https://vuejs.org) application served by [Vite](https://vite.dev). The same
codebase powers three runtime modes — `dev` (interactive presenter server), `build`
(static single-page app), and `export` (headless-browser rendering to PDF/PNG/PPTX/MD).

**Key characteristics for operators:**

- **Monorepo, several npm packages.** The product ships as `@slidev/cli` plus the
  supporting `@slidev/parser`, `@slidev/types`, `@slidev/client`, and a VS Code
  extension. (This documented repository is the engine; the end-user documentation site
  lives in a separate repository, [slidevjs/docs](https://github.com/slidevjs/docs),
  published at [sli.dev](https://sli.dev).)
- **Build tooling, not a server runtime.** There is no application server, database, or
  hosted backend. `slidev` runs a Vite dev server locally for authoring; `slidev build`
  emits static files you host anywhere; `slidev export` drives a local headless browser.
- **Config-as-code.** A deck is configured entirely from the first slide's YAML
  *headmatter* and optional `setup/*.ts` hook files in the user's project — there is no
  separate config service.
- **Node.js ≥ 20.12.0**, MIT licensed (© 2021 Anthony Fu).

---

## 2. Architecture

### 2.1 High-level data flow

```
                         slides.md (+ imported *.md via `src:`)
                                       │
                         ┌─────────────▼──────────────┐
                         │  @slidev/parser             │  parse() splits on `---`,
                         │  - parse / parseSync        │  extracts frontmatter, notes,
                         │  - load (fs, resolves src)  │  detects features, loads imports
                         │  - resolveConfig            │
                         └─────────────┬──────────────┘
                                       │ SlidevData { slides, config, headmatter, features }
                         ┌─────────────▼──────────────┐
                         │  @slidev/cli (node)         │  resolveOptions(): merge theme +
                         │  - resolveOptions / roots   │  addon + user roots; build the
                         │  - ViteSlidevPlugin         │  Vite config; define feature flags
                         └─────────────┬──────────────┘
                                       │
          ┌────────────────────────────┼────────────────────────────┐
          ▼ dev                         ▼ build                       ▼ export
   Vite dev server              vite build → dist/             Playwright Chromium
   @slidev/client SPA           static SPA + 404.html          renders /print routes →
   presenter / overview /       + _redirects + og-image        PDF / PNG / PPTX / MD
   drawing / remote control     (+ optional download PDF)      (+ outline, metadata)
```

### 2.2 Package responsibilities

| Package | Path | Responsibility |
|---|---|---|
| `@slidev/cli` | `packages/slidev` | The `slidev` binary: command parsing (yargs), option/roots resolution, the Vite plugin, and the `serve`/`build`/`export` commands. |
| `@slidev/parser` | `packages/parser` | Pure Markdown→slides parsing, frontmatter/notes extraction, feature detection, config resolution, and the `fs` loader that follows `src:` imports. No browser dependencies. |
| `@slidev/types` | `packages/types` | Shared TypeScript types: the config/headmatter schema, CLI argument shapes, setup-hook signatures, and the navigation API. The single source of truth for the configuration surface. |
| `@slidev/client` | `packages/client` | The Vue 3 front-end: the slide renderer, presenter mode, overview, drawing tool, editor, monaco/shiki/katex setup, navigation, and state. |
| Slidev for VS Code | `packages/vscode` | The editor extension: project detection, preview webview, a Volar-based language server, and AI language-model tools. |
| `create-app` / `create-theme` | `packages/create-*` | Scaffolders behind `npm init slidev` and theme creation. |

The repository is a **pnpm workspace** (`pnpm-workspace.yaml`) spanning `packages/*`,
`demo/*`, `cypress/fixtures/*`, and `docs`, with a shared dependency catalog.

### 2.3 Roots resolution model

At startup the CLI resolves a set of **roots** that are layered to produce the final
configuration and asset search path (`packages/slidev/node/options.ts`,
`resolver.ts`):

- `userRoot` — the directory containing the entry Markdown file.
- `themeRoots` — the resolved theme package (if any).
- `addonRoots` — every addon listed in the `addons` headmatter.
- `clientRoot` — `@slidev/client` itself (the built-in components/layouts).

The effective `roots` array is `[...themeRoots, ...addonRoots, userRoot]`. Vite configs,
layouts, components, and `setup/*.ts` files are discovered across all roots and merged,
so themes and addons can contribute to any of them, with the user's project taking
precedence.

### 2.4 Feature flags

The CLI computes compile-time feature flags from config and mode
(`getDefine()` in `options.ts`) and injects them as `__SLIDEV_FEATURE_*` defines:

| Define | Driven by |
|---|---|
| `__SLIDEV_FEATURE_DRAWINGS__` | `drawings.enabled` matched against the current mode. |
| `__SLIDEV_FEATURE_DRAWINGS_PERSIST__` | `drawings.persist`. |
| `__SLIDEV_FEATURE_EDITOR__` | `editor !== false` **and** dev mode only. |
| `__SLIDEV_FEATURE_RECORD__` | `record`. |
| `__SLIDEV_FEATURE_PRESENTER__` | `presenter`. |
| `__SLIDEV_FEATURE_PRINT__` | export mode, or build mode with `download` truthy. |
| `__SLIDEV_FEATURE_BROWSER_EXPORTER__` | `browserExporter`. |
| `__SLIDEV_FEATURE_WAKE_LOCK__` | `wakeLock`. |
| `__SLIDEV_HAS_SERVER__` | true except in `build` mode. |

Options whose values are `'dev'` / `'build'` enable the feature **only** in that mode —
this is how, for example, `presenter: 'dev'` keeps presenter mode out of a public build.

---

## 3. Requirements

| Requirement | Detail |
|---|---|
| Node.js | **≥ 20.12.0** (`engines.node`). Netlify builds in the repo pin Node 24. |
| Package manager | Any (npm/pnpm/yarn). The repo itself uses **pnpm** with a catalog. |
| Browser | A modern evergreen browser to view decks. |
| Export engine | `playwright-chromium` (peer/optional) — required only for `slidev export` and `build --download`. |
| Memory | Large decks build comfortably; the repo's own docs build sets `--max-old-space-size=8192` for the combined site build. |

> [!NOTE]
> `playwright-chromium` has a post-install step that downloads a browser. pnpm v11
> blocks unapproved build scripts by default; scaffolded projects allowlist it in
> `pnpm-workspace.yaml` (`allowBuilds: { playwright-chromium: true }`).

---

## 4. Configuration Reference (Headmatter)

The first slide's frontmatter is the **headmatter** and configures the whole deck. The
table below is the complete option set with the **actual runtime defaults** from
`getDefaultConfig()` (`packages/parser/src/config.ts`) and the schema in
`packages/types/src/frontmatter.ts`. Options typed `boolean | 'dev' | 'build'` enable
the feature globally (`true`), only in the dev server (`'dev'`), or only in a static
build (`'build'`).

### 4.1 Identity & metadata

| Option | Default | Type | Description |
|---|---|---|---|
| `theme` | `'default'` | string | Theme package, local path, or `none`. |
| `title` | `'Slidev'` | string | Deck title (supports inline Markdown). |
| `titleTemplate` | `'%s - Slidev'` | string | Template for the page `<title>`; `%s` is the title. |
| `author` | `''` | string | Author; embedded in exported PDF/PPTX metadata. |
| `info` | `false` | string \| boolean | Info shown on the built SPA (Markdown allowed). |
| `favicon` | jsDelivr Slidev icon URL | string | Favicon URL. |
| `seoMeta` | `{}` | object | Open Graph / Twitter card meta (`ogTitle`, `ogImage`, `twitterCard`, …). `ogImage: 'auto'` auto-generates a preview at build. |

### 4.2 Appearance & layout

| Option | Default | Type | Description |
|---|---|---|---|
| `colorSchema` | `'auto'` | `auto`/`light`/`dark`/`all` | Force or auto-detect the color scheme. |
| `aspectRatio` | `16/9` | number \| string | Slide aspect ratio (`16/9`, `1:1`, `3x4`). |
| `canvasWidth` | `980` | number | Logical canvas width in px (height derives from aspect ratio). |
| `selectable` | `false` | boolean | Whether slide text is selectable. |
| `htmlAttrs` | `{}` | object | Attributes applied to the `<html>` element. |
| `fonts` | `{}` | object | Web font config (`sans`/`serif`/`mono`/`weights`/`provider`/`local`/`fallbacks`). Default provider `google`, default weights `200,400,600`. |
| `themeConfig` | `{}` | object | Theme-specific options injected as `--slidev-theme-*` CSS vars. |
| `transition` | `null` | string \| object | Default page transition. |
| `clickAnimation` | `''` | string | Default per-click animation. |

### 4.3 Code & rendering

| Option | Default | Type | Description |
|---|---|---|---|
| `highlighter` | `'shiki'` | `'shiki'` | Syntax highlighter. `prism` is removed (throws); `shikiji` auto-migrates with a warning. |
| `twoslash` | `true` | boolean/dev/build | Enable TwoSlash type hovers in TS code. |
| `lineNumbers` | `false` | boolean | Show line numbers in code blocks. |
| `codeCopy` | `true` | boolean | Copy button on code blocks. |
| `magicMoveCopy` | `true` | boolean / `'final'` / `'always'` | Copy button behavior on Magic Move blocks. |
| `magicMoveDuration` | `800` | number | Magic Move transition duration (ms). |
| `monaco` | `true` | boolean/dev/build | Enable the Monaco editor for `{monaco}` blocks. |
| `monacoTypesSource` | `'local'` | `cdn`/`local`/`none` | Where Monaco loads type definitions from. |
| `monacoTypesAdditionalPackages` | `[]` | string[] | Extra packages to load as Monaco types. |
| `monacoTypesIgnorePackages` | `[]` | string[] | Packages (glob) to skip when loading types. |
| `monacoRunAdditionalDeps` | `[]` | string[] | Extra local modules available to `{monaco-run}`. |
| `monacoRunUseStrict` | `true` | boolean | Run `{monaco-run}` code in strict mode. |
| `plantUmlServer` | `https://www.plantuml.com/plantuml` | string | PlantUML rendering server. |
| `mdc` | `false` | boolean | **Deprecated** — use `comark`. |
| `comark` | `false` | boolean | Enable Comark (MDC-style) Markdown syntax. |
| `css` | `'unocss'` | `'unocss'` | **Deprecated/fixed** — Atomic CSS engine (only UnoCSS; other values warn and fall back). |

### 4.4 Presenting & interaction

| Option | Default | Type | Description |
|---|---|---|---|
| `presenter` | `true` | boolean/dev/build | Enable presenter mode. |
| `editor` | `true` | boolean | Enable the built-in editor (dev only at runtime). |
| `contextMenu` | `null` | boolean/dev/build/null | Enable the right-click context menu. |
| `wakeLock` | `true` | boolean/dev/build | Keep the screen awake while presenting. |
| `record` | `'dev'` | boolean/dev/build | Enable camera recording. |
| `drawings` | `{}` | object | Drawing tool: `enabled` (default `true`), `persist` (default `false`, or a path), `presenterOnly` (default `false`), `syncAll` (default `true`). |
| `duration` | `'30min'` | string \| number | Expected talk duration (for the timer). |
| `timer` | `'stopwatch'` | `stopwatch`/`countdown` | Presenter timer mode. |
| `notesAutoRuby` | `{}` | object | Map of words → ruby readings, auto-applied to notes. |
| `remote` | `false` | string \| boolean | Listen on the network; a string sets the presenter password. |

### 4.5 Build, export & routing

| Option | Default | Type | Description |
|---|---|---|---|
| `routerMode` | `'history'` | `hash`/`history` | Vue Router mode. Use `hash` for sub-directory deploys. |
| `download` | `false` | boolean \| string | Show a download button on the built SPA; a string is a custom PDF URL. |
| `exportFilename` | `''` | string | Force the export filename (extension auto-added). |
| `export` | `{}` | object | Default export options applied to the `export` command. |
| `browserExporter` | `'dev'` | boolean/dev/build | Enable the in-browser `/export/` exporter. |
| `remoteAssets` | `false` | boolean/dev/build | Download remote assets locally via `vite-plugin-remote-assets`. |
| `preloadImages` | `true` | boolean \| `{ ahead?: number }` | Preload upcoming slide images (default look-ahead 3). |
| `addons` | `[]` | string[] | Slidev addons to load. |

> [!IMPORTANT]
> Changing any of `monaco`, `routerMode`, `fonts`, `css`, `mdc`, `editor`, `theme`, or
> `seoMeta` in the headmatter triggers a **dev-server restart** (these are in
> `CONFIG_RESTART_FIELDS`). A theme change or a katex/monaco feature change also
> restarts. This is expected behavior, not a fault.

### 4.6 Per-slide frontmatter

Every non-first slide accepts the per-slide keys (`layout`, `class`, `transition`,
`clicks`, `clicksStart`, `hide`/`disabled`, `hideInToc`, `title`, `level`, `routeAlias`,
`zoom`, `src`, `image`/`background`, `dragPos`, …). These are documented in the User
Guide, §3.2. `disabled`/`hide` slides are dropped from the rendered `slides` array;
`src` slides are replaced by the imported file's slides.

---

## 5. The Rendering Pipeline (Internals)

### 5.1 Parsing (`@slidev/parser`)

`parse()` (`core.ts`) splits the Markdown on lines equal to `---`, while correctly
skipping `---` inside fenced code blocks and HTML comments. For each slide it:

- Extracts frontmatter (either a `---` fenced block or a leading ```` ```yaml ```` block).
- Detects a trailing HTML comment as the slide's **note**.
- Derives `title`/`level` from frontmatter or the first heading.
- Extracts image URLs from frontmatter, Markdown, Vue props, and CSS `url()` (so build's
  content stripping doesn't lose them).
- Computes a content `revision` hash for HMR.

`detectFeatures()` scans the combined source for KaTeX (`$…$`/`$$`), Monaco
(`{monaco…}`), Mermaid (```` ```mermaid ````), and Tweet/Bluesky tags, returning a
`SlidevDetectedFeatures` record that gates which heavy machinery loads.

`load()` (`fs.ts`) resolves `src:` imports recursively (with `#range` slicing via
`parseRangeString`), applies frontmatter overrides down the import chain, records
`watchFiles` for HMR, and runs any preparser extensions.

`resolveConfig()` (`config.ts`) merges, in increasing precedence:
default config → theme `defaults` → `headmatter.config` → headmatter top-level — then
resolves fonts, drawings, `htmlAttrs`, and `themeConfig` sub-objects and normalizes the
aspect ratio. `verifyConfig()` warns on theme/highlighter/color-schema mismatches.

### 5.2 Serving (`commands/serve.ts`)

`createServer()` builds a Vite dev server from the merged config
(`resolveViteConfigs()` in `shared.ts`), which loads every root's `vite.config.*`,
merges them, and mounts `ViteSlidevPlugin`. The dev command (`cli.ts`) wires a
`loadData` callback that re-parses on change and decides whether to hot-update or restart
(theme/config/feature changes force a restart). A `chokidar` watcher restarts on
add/remove of watched setup/config files.

### 5.3 Building (`commands/build.ts`)

`build()` runs `vite build` with the generated `index.html`, then post-processes the
output directory:

- Generates or copies the SEO `ogImage` (rendering one slide to PNG via the exporter when
  `ogImage: 'auto'`).
- Copies `index.html` → `404.html` (so GitHub Pages serves the SPA on deep links).
- Writes a `_redirects` file (`{base}* {base}index.html 200`) for Netlify-style SPA
  routing, unless one already exists.
- When `download` is truthy, serves `dist/` locally with `sirv` and runs the exporter to
  bundle a downloadable PDF.

### 5.4 Exporting (`commands/export.ts`)

Export launches **Playwright Chromium** and navigates the running server's print routes:

- **Formats:** `pdf` (default), `png`, `pptx`, `md`. PPTX renders slides to PNG and
  assembles a deck with `pptxgenjs`, carrying titles/notes; MD emits image references plus
  notes.
- **One-piece vs `--per-slide`:** by default the whole deck renders as one print page;
  `--per-slide` renders each slide separately (better for some global components but
  breaks cross-slide links and the PDF outline).
- **Clicks:** `--with-clicks` drives `ArrowRight` to capture each animation step.
- **TOC:** `--with-toc` builds a nested outline from slide titles/levels and injects it
  with `@lillallol/outline-pdf`.
- **Metadata:** the first slide's `title`/`author`/`info`/`keywords` are written into the
  PDF metadata.
- **Robust waits:** the renderer waits for `.slidev-slide-loading` to clear, honors
  `data-waitfor`, waits for iframes and Mermaid containers, and hides Monaco aria nodes
  before capture. `--wait`/`--wait-until`/`--timeout` tune timing.

`playwright-chromium` is resolved from the user root, the workspace root, the global
registry, and finally the CLI's own install — failing with an actionable install message.

---

## 6. Customization & Extension

### 6.1 Setup hooks

Place TypeScript files under `setup/` in your project (or theme/addon) and export a
`define*Setup` function. These are the supported hooks (`packages/types/src/setups.ts`):

| File / hook | Customizes |
|---|---|
| `setup/shiki.ts` → `defineShikiSetup` | Shiki languages, themes, transformers. |
| `setup/katex.ts` → `defineKatexSetup` | KaTeX options. |
| `setup/unocss.ts` (`uno.config.ts`) → `defineUnoSetup` | UnoCSS configuration. |
| `setup/monaco.ts` → `defineMonacoSetup` | Monaco editor options. |
| `setup/main.ts` → `defineAppSetup` | Vue app/router setup (install plugins). |
| `setup/root.ts` → `defineRootSetup` | Root-component setup. |
| `setup/routes.ts` → `defineRoutesSetup` | Modify the generated routes. |
| `setup/mermaid.ts` → `defineMermaidSetup` / `defineMermaidRendererSetup` | Mermaid config or a custom renderer. |
| `setup/shortcuts.ts` → `defineShortcutsSetup` | Add/replace keyboard shortcuts (receives the nav API). |
| `setup/transformers.ts` → `defineTransformersSetup` | Markdown/code-block transformers. |
| `setup/preparser.ts` → `definePreparserSetup` | Preparser extensions (transform raw lines/slides/notes before parsing). |
| `setup/vite-plugins.ts` → `defineVitePluginsSetup` | Inject custom Vite plugins. |
| `setup/code-runners.ts` → `defineCodeRunnersSetup` | Register runners for `{*-run}` blocks. |
| `setup/context-menu.ts` → `defineContextMenuSetup` | Customize the right-click menu. |

> [!WARNING]
> Editing `setup/shiki.ts`, `setup/katex.ts`, `setup/preparser.ts`,
> `setup/transformers.ts`, `setup/unocss.ts`, `setup/vite-plugins.ts`, `uno.config.ts`,
> `unocss.config.ts`, or `vite.config.*` triggers a full dev-server restart (they are in
> `FILES_CHANGE_RESTART`). HMR does not apply to these.

### 6.2 Layouts and components

Any `.vue` file in a `layouts/` folder across the roots becomes a usable `layout:` value
(layouts are globbed and cached). Components in a `components/` folder are auto-imported
(via `unplugin-vue-components`) and usable directly in Markdown.

### 6.3 Code runners

`{monaco-run}` and custom `{lang-run}` blocks dispatch to runners registered via
`defineCodeRunnersSetup`. A runner returns HTML, text, a DOM element, or an error. The
context provides a `highlight()` helper and a `run()` to delegate to other runners.

> [!CAUTION]
> A runner's `CodeRunnerOutputHtml` is rendered **without sanitization** — Slidev's own
> type docs state it does not sanitize the HTML for you. Only return HTML from trusted
> sources, or sanitize it in your runner. See §8.

### 6.4 Themes and addons

- **Themes** are npm packages named `@slidev/theme-*` (official) or `slidev-theme-*`
  (community), or local folders. A theme's `package.json` carries a `slidev` field
  (`SlidevThemeMeta`) with `defaults`, `colorSchema`, and `highlighter`.
- **Addons** are listed in the `addons` headmatter and contribute slides, components,
  layouts, and setup hooks via the same roots mechanism.
- `slidev theme eject` copies the active theme into a local `theme/` folder and rewrites
  the deck's `theme:` to point at it.

---

## 7. Operations

### 7.1 Running modes

| Command | Mode | Notes |
|---|---|---|
| `slidev [entry]` | dev | Vite dev server, default port `3030` (auto-scans `3030–4000`). Prints public/presenter/overview/export URLs. |
| `slidev build [entry..]` | build | Emits a static SPA to `dist/` (or `--out`). Accepts multiple entries. |
| `slidev export [entry..]` | export | Headless render to PDF/PNG/PPTX/MD. |
| `slidev export-notes [entry..]` | export | Renders presenter notes to PDF. |
| `slidev format [entry..]` | — | Re-serializes Markdown through the parser. |
| `slidev theme eject` | — | Ejects the theme locally. |

### 7.2 Environment variables

| Variable | Effect |
|---|---|
| `EDITOR` | Editor opened by the dev `e` shortcut and by the editor integration; defaults to `code` (VS Code). Set in `serve.ts`. |

There are no required secrets or service credentials — Slidev has no backend.

### 7.3 Networking & remote control

`--remote` switches the listen host to `--bind` (default `0.0.0.0`) and enables
presenter remote control; pass `--remote <password>` to gate presenter mode. `--tunnel`
opens a Cloudflare Quick Tunnel (requires `--remote`) for a public URL. The startup
banner enumerates LAN URLs and the public IP; the `c` shortcut prints a QR code.

### 7.4 Global installs

The resolver (`resolver.ts`) deliberately handles global installs across npm/yarn and
pnpm v11's isolated globals layout, discovering sibling install groups so a globally
installed theme can be found alongside a globally installed `@slidev/cli`. This matters
when operators install Slidev with `pnpm add -g`.

---

## 8. Security

Slidev produces and runs web content; treat decks like code. Operator-relevant
considerations, grounded in the source:

- **Theme/addon name validation.** Package names are validated against a strict npm-name
  regex (`RE_SAFE_PKG_NAME`) before resolution. Names with shell metacharacters,
  backslashes, uppercase letters, parent-traversal segments, or extra slashes are
  rejected with `Invalid theme/addon name … Only valid npm package names are allowed.`
  This blocks command/path injection via the `theme`/`addons` config.
- **Interactive install prompts.** Missing themes/addons (and a missing entry file)
  trigger an install/create prompt only in an interactive TTY; non-interactive
  environments hard-fail instead of running an unattended install — safe for CI.
- **Unsanitized HTML in code-runner output.** Custom runners that return
  `CodeRunnerOutputHtml` are rendered as-is (no sanitization). Only enable trusted
  runners; sanitize untrusted output yourself.
- **Arbitrary code execution by design.** `{monaco-run}` executes code in the
  browser; `monacoRunUseStrict` (default true) only sets strict mode. This is intended
  for trusted, author-controlled decks — do not load untrusted slide sources.
- **Remote exposure.** `--remote` exposes presenter control on the network and
  `--tunnel` publishes a public URL. Always set a presenter password on untrusted
  networks; tunnels last only for the session.
- **External services.** PlantUML rendering posts diagram source to
  `plantUmlServer` (default the public `plantuml.com` server) — point it at a
  self-hosted instance for sensitive content. `remoteAssets` and the default `favicon`
  fetch from remote URLs/CDNs; set `remoteAssets` and a local favicon to keep builds
  self-contained. Web fonts default to Google Fonts (`fonts.provider`); use
  `provider: none` or `fonts.local` to avoid third-party font requests.

> [!IMPORTANT]
> Slidev has no authentication, authorization, or multi-tenant model — it is a local
> authoring tool and a static-site generator. Security of a published deck is the
> security of the static host you deploy it to.

---

## 9. Deployment

`slidev build` emits a self-contained static SPA. Deploy `dist/` to any static host.

### 9.1 Sub-path deploys (GitHub Pages, project sites)

```bash
slidev build --base /my-talk/ --router-mode hash
```

`--base` must start and end with `/`. `hash` routing avoids server-side rewrite
requirements; the emitted `404.html` covers history-mode deep links on GitHub Pages.

### 9.2 Netlify

Scaffolded projects ship a `netlify.toml`:

```toml
[build]
publish = "dist"
command = "npm run build"

[build.environment]
NODE_VERSION = "20"
```

The build also writes a `_redirects` file for SPA fallback.

### 9.3 Vercel

Scaffolded projects ship a `vercel.json` that rewrites all routes to `index.html`:

```json
{
  "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }],
  "buildCommand": "npm run build",
  "outputDirectory": "dist"
}
```

### 9.4 Deployment checklist

- [ ] `slidev build` succeeds locally and `dist/` renders when served statically.
- [ ] `--base` set (with leading/trailing `/`) if deploying under a sub-path.
- [ ] `--router-mode hash` for GitHub Pages project sites (or rely on `404.html`).
- [ ] `--without-notes` if speaker notes must not ship publicly.
- [ ] `--download` (or `download: true`) if you want a downloadable PDF + button.
- [ ] SEO `seoMeta`/`ogImage` set for link previews.
- [ ] Node version on the host ≥ 20.12.0.

---

## 10. Developer Internals

### 10.1 Repository layout

| Path | Contents |
|---|---|
| `packages/slidev/node` | CLI, command implementations, options/roots resolution, Vite plugin, syntax/vite/virtual sub-systems. |
| `packages/parser/src` | `core.ts` (parse), `fs.ts` (load + imports), `config.ts` (defaults + resolve), `utils.ts` (ranges, aspect ratio), `timesplit`. |
| `packages/types/src` | Config/frontmatter/cli/setups/options/clicks/code-runner/toc/transform/vite types. |
| `packages/client` | Vue front-end: `internals/`, `setup/`, `state/`, `styles/`, `layouts/`, `builtin/`, `composables/`, `logic/`, `modules/`, `pages/`. |
| `packages/vscode` | Extension `src/`, Volar `language-server/`, schema/syntaxes. |
| `packages/create-app`, `packages/create-theme` | Scaffolders + templates. |
| `demo/*` | Example decks used as fixtures and showcases. |
| `docs/` | VitePress source for sli.dev (separate publishable site). |
| `cypress/`, `test/` | E2E (Cypress, baseUrl `:3041`) and unit tests (Vitest). |

### 10.2 Build & tooling

- **Bundling:** `tsdown` (ESM, `target: node20`, dts) for the node packages.
- **Tests:** Vitest projects across `packages/*` and `test`; Cypress E2E against a fixture
  served on port 3041.
- **Lint/format:** `@antfu/eslint-config`, including a Slidev formatter for `slides.md`
  fixtures.
- **Release:** `bumpp`, with `ovsx`/`vsce` for the VS Code extension; `taze` for dep
  bumps.
- **Key runtime deps:** Vue 3, Vue Router, Vite, Shiki (+ TwoSlash, magic-move), Monaco,
  KaTeX, Mermaid, UnoCSS, yargs, chokidar, Playwright (export), pdf-lib + outline-pdf,
  pptxgenjs, Drauu (drawings), RecordRTC (recording), `@vueuse/*`.

### 10.3 Code patterns worth knowing

- **Config-as-code with a single schema source.** `HeadmatterConfig`/`Frontmatter` in
  `@slidev/types` is the canonical schema; the VS Code extension even generates its
  `headmatter.json` JSON-schema from it (`packages/vscode/scripts/schema.ts`), so editor
  validation never drifts from the runtime.
- **Mode-aware feature gating.** The `'dev'`/`'build'` tri-state on many options, resolved
  into `__SLIDEV_FEATURE_*` defines, keeps the client bundle lean and lets a single
  config express "dev-only" behavior.
- **Reactive VS Code extension.** The extension uses `reactive-vscode` (Vue reactivity in
  the extension host) and a Volar language server, with project state as reactive
  `Map`s/refs.

### 10.4 Testing strategy

Parser logic is unit-tested (range parsing, aspect ratios, resolver path logic with a
mocked filesystem — note the extensive `findInvocationNodeModulesPath` /
`computeInvocationSearchPaths` suites for pnpm/npm/yarn global-install layouts). E2E
coverage runs through Cypress against a built fixture. New CLI flags or config keys should
add a parser/option test and, ideally, an E2E assertion.

---

## 11. Known Issues & Improvement Areas

Sourced from the issue tracker (community-confirmed) at the documented commit:

| Area | Signal | Reference |
|---|---|---|
| ESM/CJS interop | Dev server imports `@fix-webm-duration/fix` as a named ESM export. | [#2617](https://github.com/slidevjs/slidev/issues/2617), [#2569](https://github.com/slidevjs/slidev/issues/2569) |
| Magic Move | Code-snippet inclusions in Magic Move regressed in a recent release. | [#2586](https://github.com/slidevjs/slidev/issues/2586) |
| Theme assets in pnpm workspaces | `vite-plugin-static-copy` path resolution for theme `public/` assets. | [#2607](https://github.com/slidevjs/slidev/issues/2607) |
| Windows first-run | Scaffolder uses `slidev --open`, which can fail on Windows. | [#2582](https://github.com/slidevjs/slidev/issues/2582) |
| Public assets | Loading `public/` assets outside frontmatter. | [#2616](https://github.com/slidevjs/slidev/issues/2616) |
| PDF reactivity | `useNav().currentSlideNo` not reactive during PDF export. | [#1776](https://github.com/slidevjs/slidev/issues/1776) |
| One-piece PDF | Named-slot `<img>` can disappear with container padding. | [#2588](https://github.com/slidevjs/slidev/issues/2588) |

Frequently-requested capabilities include multi-presenter sync ([#2611](https://github.com/slidevjs/slidev/issues/2611)),
single-file HTML export ([#2581](https://github.com/slidevjs/slidev/issues/2581)), and
VS Code slide reordering/deletion ([#2529](https://github.com/slidevjs/slidev/issues/2529)).

> [!NOTE]
> Two config fields are marked deprecated in the schema and should be migrated:
> `css` (fixed to `unocss`) and `mdc` (replaced by `comark`). The `prism` highlighter is
> fully removed and throws.

---

## 12. Quick Reference

### Commands

```text
slidev [entry]            # dev server (default)
slidev build [entry..]    # static SPA → dist/
slidev export [entry..]   # PDF/PNG/PPTX/MD
slidev export-notes …     # notes → PDF
slidev format [entry..]   # reformat markdown
slidev theme eject        # eject theme locally
```

### Dev URLs (default port 3030)

```text
/            public slide show
/presenter/  presenter mode
/overview/   slides overview
/export/     in-browser exporter
/__inspect/  Vite inspector (with --inspect)
```

### Restart triggers (dev)

```text
config:  monaco, routerMode, fonts, css, mdc, editor, theme, seoMeta (+ theme/katex/monaco feature change)
files:   setup/{shiki,katex,preparser,transformers,unocss,vite-plugins}.ts,
         uno.config.ts, unocss.config.ts, vite.config.*  (+ add/remove of watched files)
```

### Build artifacts

```text
dist/index.html, dist/404.html (GH Pages), dist/_redirects (Netlify SPA),
og-image (when seoMeta.ogImage), optional downloadable PDF (--download)
```

### Key paths

```text
packages/slidev/node/cli.ts          command definitions (yargs)
packages/parser/src/config.ts        getDefaultConfig() — defaults
packages/types/src/frontmatter.ts    HeadmatterConfig — schema + docs
packages/slidev/node/commands/*.ts   serve / build / export
```

---

_Generated June 2026 from slidevjs/slidev at a1609ff. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
