Getting started
You need Node 20.19 or newer (Vite 8’s floor) and a package manager — npm, pnpm, Yarn or Bun all work.
Create a deck
Section titled “Create a deck”npm create @kadal/deck@latest my-talkcd my-talknpm installnpm run devVite prints the address; open it and press ? for the keys. Edit deck.mdx and the slide
you are looking at updates in place — each slide is its own module, so a change re-renders one slide,
not the deck.
What is in the folder
Section titled “What is in the folder”my-talk/├── deck.mdx the talk: headmatter, then slides split at ---├── deck.config.ts engine settings that override the headmatter├── glossary.yaml optional: term cards (see Glossary term cards)├── index.html the page Vite serves├── src/│ ├── main.tsx mounts <Deck>, the pages and the live provider│ ├── components.tsx your own components, usable in deck.mdx without imports│ └── styles.css your CSS on top of the theme├── public/ images, video, posters — served as-is├── vite.config.ts the deck() plugin and React└── package.jsonsrc/main.tsx is short, and the whole engine hangs off it:
import { Deck } from "@kadal/deck";import "@kadal/deck/styles.css";import "@kadal/deck/themes/minimal.css";import "@kadal/deck/backgrounds";import { AudiencePage } from "@kadal/deck/audience";import { LiveProvider } from "@kadal/deck/live";import { NotesPage, PresenterPage, StatsPage } from "@kadal/deck/presenter";import { StrictMode } from "react";import { createRoot } from "react-dom/client";import manifest from "virtual:deck";
import { config } from "../deck.config.ts";import { components } from "./components.tsx";import "./styles.css";
createRoot(document.getElementById("root")!).render( <StrictMode> <Deck manifest={manifest} config={config} components={components} pages={{ presenter: PresenterPage, audience: AudiencePage, notes: NotesPage, stats: StatsPage, }} > <LiveProvider /> </Deck> </StrictMode>,);virtual:deck is what the Vite plugin makes of deck.mdx: the configuration, one lazily loaded
module per slide and per slide’s notes, and the glossary.
import { deck } from "@kadal/deck/vite";import react from "@vitejs/plugin-react";import { defineConfig } from "vite";
export default defineConfig({ plugins: [deck({ entry: "deck.mdx" }), react()],});The pages you get
Section titled “The pages you get”Every deck is a small app with several views, all served from the same build:
| Path | What |
|---|---|
/ |
the deck |
/presenter |
the presenter view: current and next slide, notes, timer, tools, the room |
/join |
the audience page for phones |
/notes |
every note on one page |
/stats |
views, time per slide, sessions, questions |
/?print-pdf |
every slide as a page, for printing |
/?view=scroll |
the deck as one scrolling page |
npm run build # static files in dist/dist/ is a static site: put it on any host. To get a link and a live room without hosting anything,
publish it:
npx kadal-deck loginnpx kadal-deck publishThe live room in development
Section titled “The live room in development”The live features — following, polls, questions, reactions — talk to a room server at
live.api (/api by default). In development, either proxy /api to a room you host
(Self-hosting the room), or set live: { api: null } in
deck.config.ts for an offline deck. A published deck on deck.kadal.cc gets its room automatically.