Aller au contenu principal

3. Embed Slidev decks

Slidev is a Vue-based slide framework. It is a separate app, so the approach is: build each deck to a static site under static/, then embed it with an iframe.

The slides workspace​

Decks live in their own npm workspace under slides/, one package per deck. This lets each deck pin its own Slidev version and dependencies independently.

slides/package.json:

{
"name": "docusaurus-slidev",
"private": true,
"workspaces": ["decks/*"]
}

Write a deck​

Each deck is a workspace package at slides/decks/<name>/ with its own package.json and a slides.md.

slides/decks/intro/package.json:

{
"name": "intro",
"private": true,
"scripts": {
"dev": "slidev --open",
"build": "slidev build",
"export": "slidev export"
},
"devDependencies": {
"@slidev/cli": "^52.0.0",
"@slidev/theme-default": "latest"
}
}

slides/decks/intro/slides.md (slides separated by ---):

---
theme: default
title: Welcome
---

# Slidev in Docusaurus

Press space to advance

---

# A second slide

- Markdown-based
- Embeddable

Install the workspace once from slides/:

npm install

Build script​

Docusaurus serves everything in static/ verbatim, so build each deck into static/slides/<name>/. The site's scripts/build-slidev.mjs:

  • discovers each slides/decks/<name>/slides.md,
  • runs that deck's own npm run build (so it uses the deck's pinned Slidev version) with these flags forwarded to slidev build:
    • --out → static/slides/<name>/ (where Docusaurus serves it),
    • --base → <baseUrl>/slides/<name>/ (so assets resolve on GitHub Pages),
    • --router-mode hash → avoids server rewrites for a subdirectory deploy, only for Slidev versions that support the flag (older versions error on unknown args, so the script probes build --help first),
  • and writes src/slides-manifest.json for the decks index.

The site's slidev:build script simply runs it:

{ "scripts": { "slidev:build": "node scripts/build-slidev.mjs" } }

:::note Mixed Slidev versions Because each deck pins its own version, one deck can be on Slidev v51 and another on v52. The build script only passes --router-mode to decks whose Slidev supports it, so mixed versions build side by side. :::

The embed component​

Create src/components/Slides/index.tsx:

import React from 'react';
import useBaseUrl from '@docusaurus/useBaseUrl';

export default function Slides({ name }: { name: string }) {
const src = useBaseUrl(`/slides/${name}/`);
return (
<iframe
src={src}
title={`Slidev deck: ${name}`}
style={{
width: '100%',
aspectRatio: '16 / 9',
border: '1px solid var(--ifm-color-emphasis-300)',
}}
allow="fullscreen"
loading="lazy"
/>
);
}

Use it in a page​

import Slides from '@site/src/components/Slides';

<Slides name="intro" />

Open the deck [full screen](pathname:///slides/intro/).

:::warning Linking to the deck Use the pathname:// prefix for the full-screen link. A normal link to /slides/intro/ is a static asset, not a Docusaurus route, so onBrokenLinks: 'throw' would fail the build. :::

Hosting multiple decks​

Add another workspace package (slides/decks/<name>/ with a package.json and slides.md), run npm install in slides/, and it builds to /slides/<name>/ automatically. The build script also writes a src/slides-manifest.json listing each deck's name, title (from frontmatter), and path, which a small SlidesIndex component renders as a gallery linking to every deck.

Since CI installs the site and the slides workspace separately, remember to run npm ci in slides/ in the deploy workflow before building the site.