Skip to main content

Múltiplos decks Slidev em um repositório

Este guia mostra como manter vários decks do Slidev em um único repositório, publicá-los no GitHub Pages em subcaminhos independentes e embutir a URL já publicada em uma página do Docusaurus.

Como funciona​

O Slidev builda um deck por vez. Para ter vários no mesmo repositório, cada deck é buildado com seu próprio base path (--base) e sua própria pasta de saída (--out), e tudo é reunido em um único diretório dist/. O resultado publicado fica assim:

https://<usuario>.github.io/<repo>/ → índice com links
https://<usuario>.github.io/<repo>/slide01/ → deck 1
https://<usuario>.github.io/<repo>/slide02/ → deck 2

Estrutura do repositório​

<repo>/
├── decks/
│ ├── slide01/slides.md
│ └── slide02/slides.md
├── build.mjs # builda todos os decks + gera o índice
├── package.json
└── .github/workflows/deploy.yml

Cada deck é apenas uma pasta com um slides.md. Para adicionar um novo deck, basta criar decks/slide03/slides.md — nada mais precisa ser editado.

Nomes viram URLs

O nome da pasta do deck vira o subcaminho na URL. Prefira nomes descritivos (introducao, protocolos) a números — a URL fica mais legível.

Inicialização do repositório​

Crie um novo repositório no GitHub, adicione os arquivos e rode os comandos abaixo para inicializar o projeto:

pnpm install
git init && git add . && git commit -m "Setup multi-deck Slidev"
git branch -M main
git remote add origin https://<usuario>.github.io/<repo>/.git
git push -u origin main

Configuração de cada deck​

No headmatter (primeiro bloco ---) de cada slides.md, use roteamento por hash. Isso evita erro 404 ao atualizar (F5) em um slide interno e é essencial quando o deck for embutido em um iframe.

---
routerMode: hash
theme: seriph
title: Introdução ao Slidev
---
# Primeiro slide
Por que hash?

Com roteamento por hash a URL do slide fica após o # (.../slide01/#/3). Tudo depois do # não é enviado ao servidor, então o GitHub Pages sempre entrega o index.html do deck e o slide correto é resolvido no navegador. Sem isso, atualizar em um slide interno retorna 404.

Script de build​

O build.mjs detecta cada decks/*/slides.md, builda com o base path correto e gera uma página índice em dist/index.html. Em CI, o nome do repositório vem da variável GITHUB_REPOSITORY, então o base path se ajusta automaticamente.

build.mjs
import { execSync } from "node:child_process";
import {
readdirSync,
rmSync,
writeFileSync,
existsSync,
statSync,
} from "node:fs";
import { join, resolve } from "node:path";

const REPO = process.env.GITHUB_REPOSITORY?.split("/")[1] || "iiot-slides";
const DECKS_DIR = "decks";
const OUT_DIR = "dist";

if (existsSync(OUT_DIR)) rmSync(OUT_DIR, { recursive: true, force: true });

const decks = readdirSync(DECKS_DIR).filter((name) => {
const p = join(DECKS_DIR, name);
return statSync(p).isDirectory() && existsSync(join(p, "slides.md"));
});

for (const deck of decks) {
const entry = join(DECKS_DIR, deck, "slides.md");
const base = `/${REPO}/${deck}/`;
const out = resolve(OUT_DIR, deck); // absoluto: slidev resolve --out relativo ao slides.md
execSync(`npx slidev build ${entry} --base ${base} --out ${out}`, {
stdio: "inherit",
});
}

const links = decks
.map((d) => ` <li><a href="./${d}/">${d}</a></li>`)
.join("\n");
writeFileSync(
join(OUT_DIR, "index.html"),
`<!doctype html>
<html lang="pt-BR"><head><meta charset="utf-8"><title>Slides</title></head>
<body><h1>Apresentações</h1><ul>\n${links}\n</ul></body></html>\n`,
);
Caminho de saída

O --out do Slidev é resolvido relativo ao slides.md, não à raiz do projeto. Por isso o script usa um caminho absoluto (resolve(...)) — passar dist/slide01 direto faria a saída ir parar em decks/slide01/dist/.

Publicação no GitHub Pages​

Use o workflow oficial do Slidev adaptado para rodar o build.mjs:

.github/workflows/deploy.yml
name: Deploy pages

on:
workflow_dispatch: {}
push:
branches: [main]

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: false

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "lts/*"
- run: npm i -g @antfu/ni
- run: nci
- name: Build all decks
run: node build.mjs
- uses: actions/configure-pages@v5
- uses: actions/upload-pages-artifact@v3
with:
path: dist

deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
steps:
- id: deployment
uses: actions/deploy-pages@v4
Habilite o Pages manualmente

Antes do primeiro deploy, vá em Settings → Pages → Source e selecione GitHub Actions. Não use enablement: true no passo configure-pages — o token do workflow normalmente não tem permissão para criar o site, o que causa o erro Resource not accessible by integration.

Embutir a URL já publicada​

Com os decks no ar, embuta qualquer um em uma página .mdx do Docusaurus usando um iframe. Em MDX o style é um objeto JavaScript:

<iframe
src="https://<usuario>.github.io/<repo>/slide01/"
style={{ width: "100%", aspectRatio: "16 / 9", border: 0, borderRadius: 8 }}
allowFullScreen
/>

Para abrir em um slide específico, aproveite o roteamento por hash:

<iframe
src="https://<usuario>.github.io/<repo>/slide02/#/3"
style={{ width: "100%", aspectRatio: "16 / 9", border: 0, borderRadius: 8 }}
allowFullScreen
/>

Componente reutilizável (opcional)​

Para não repetir o iframe, crie um componente em src/components/SlideEmbed.jsx:

src/components/SlideEmbed.jsx
import React from "react";

const BASE = "https://<usuario>.github.io/<repo>";

export default function SlideEmbed({ deck, slide = 1, aspect = "16 / 9" }) {
const src = `${BASE}/${deck}/#/${slide}`;
return (
<div style={{ margin: "1.5rem 0" }}>
<iframe
src={src}
title={`Slides: ${deck}`}
loading="lazy"
allowFullScreen
style={{
width: "100%",
aspectRatio: aspect,
border: "1px solid var(--ifm-color-emphasis-300)",
borderRadius: 8,
}}
/>
<p>
<a href={src} target="_blank" rel="noreferrer">
Abrir em nova aba ↗
</a>
</p>
</div>
);
}

E use em qualquer .mdx:

import SlideEmbed from "@site/src/components/SlideEmbed";

<SlideEmbed deck="slide01" />
<SlideEmbed deck="slide02" slide={3} />
Link direto, sem iframe

Para apenas linkar um deck (sem embutir), use um link normal: [Ver apresentação](https://<usuario>.github.io/<repo>/slide01/).