Skip to content

Migrate a Slidev deck

Kadal Deck reads much of Slidev’s syntax as it is: the separators, headmatter and frontmatter, block frontmatter, defaults, comment notes with [click] markers, src: includes with ranges, slots, the layout names, scoped <style>, the code-fence options, magic move, code groups, snippets, TwoSlash, Monaco, Mermaid and PlantUML fences, stepped maths, Comark (MDC), icon tags, v-drag, and the five official themes. What differs is underneath: a Kadal deck is MDX with React components, where a Slidev deck is Markdown with Vue. The importer does that translation for you.

From the folder that holds your Slidev project:

Terminal window
npx kadal-deck import slidev ./my-talk
Terminal window
pnpm exec kadal-deck import slidev ./my-talk
Terminal window
yarn kadal-deck import slidev ./my-talk
Terminal window
bunx kadal-deck import slidev ./my-talk

It reads ./my-talk (a folder, or its slides.md) and writes a new deck folder beside it, ./my-talk-kadal, made the way kadal-deck new makes one. Nothing in the Slidev project is changed, installed or run. Then it runs kadal-deck check on the result and prints a summary:

Imported my-talk/slides.md into my-talk-kadal.
4 slides, 4 (100%) with nothing to report.
theme: seriph → seriph (@kadal/theme-seriph)
report: 0 errors, 1 warnings, 0 notes — IMPORT-REPORT.md
check: 0 errors, 1 warnings (kadal-deck check in the folder lists them); 2 packages to install
Next:
cd my-talk-kadal
npm install
npm run dev
Flag What it does
--out <dir> (-o) where the new deck goes (default <folder>-kadal)
--force write into a folder that isn’t empty
--install install the new deck’s packages straight away; --pm npm|pnpm|yarn|bun picks the package manager
--json one JSON object on stdout, for scripts

Slidev’s own starter deck converts with 16 of its 17 slides needing nothing further.

File From
deck.mdx slides.md, converted; files it includes with src: land in pages/, converted too
src/components/*.tsx, src/layouts/*.tsx, src/global-top.tsx… your .vue components, layouts and global layers, as React
src/custom-nav-controls.tsx custom-nav-controls.vue
src/components.tsx registers the converted components, so slides use them without imports
src/slidev.css the utility classes your slides use (UnoCSS spellings), at Kadal’s scale
src/slidev.tsx the few helpers converted components need (Slidev’s click handlers, for instance)
src/styles/*.css your styles/, with @apply resolved
public/, snippets/ copied as they are
package.json the starter’s, plus the Kadal port of your theme, an @iconify-json/<set> for each icon set, and your export settings
IMPORT-REPORT.md everything that needs a look, slide by slide, worst first
  • The headmatter: your theme becomes its Kadal port (default → classic, apple-basic → crisp), colorSchema → colorScheme, transitions (Slidev’s way out of a slide becomes Kadal’s way in), drawings (persist: true → persist: "files"), canvas size, fonts, defaults, mdc: true → comark: true, the Monaco type settings (monacoTypesSource, monacoTypesAdditionalPackages, monacoTypesIgnorePackages) and the export options.
  • Clicks: every v-click, v-after, v-clicks, v-switch, v-mark, v-motion and v-drag, with their values and modifiers, and their component forms. Click counts are written as explicit steps wherever Slidev and Kadal count differently.
  • Template values: {{ $slidev.nav.currentPage }} and the other navigation values, {{ $clicks }}, $clicksContext.current and v-if="$clicks > n" chains (as <ClickCount>), the slide’s <script setup> constants, v-for and v-if that can be decided at import, :prop bindings and $slidev.nav click handlers.
  • Slidev’s built-in components by their Kadal names (table below), icons, code-fence options.
  • The project’s Vue files: components, layouts, global-top.vue, global-bottom.vue and custom-nav-controls.vue become TSX. State (ref), props and simple handlers carry over.
  • Lengths are rescaled from Slidev’s 980 px canvas to Kadal’s 1920 × 1080 stage, so the slides keep their proportions.

IMPORT-REPORT.md gives each item a severity: an error means the slide doesn’t show what the Slidev one did (the source is kept beside it in an MDX comment, {/* slidev: … */}); a warning means it shows, but differently; a note means it was carried over in Kadal’s spelling, or dropped because Kadal does it another way. Typical entries:

  • a Vue component with real logic (watchers, emits, composables) becomes a stub that renders its markup, with a TODO at the top;
  • classes that are neither UnoCSS utilities the importer knows nor defined in your styles;
  • {monaco-run} blocks that import something: a package import loads from esm.sh when the block runs, a local file does not, so inline it or register a runner for it.

Work through the errors first, then run npx kadal-deck checkpnpm exec kadal-deck checkyarn kadal-deck checkbunx kadal-deck check and npm run devpnpm devyarn devbun run dev and look at every slide.

The converter is also a library, convertSlidev from @kadal/deck/import, a pure function for tools that convert in the browser.

  1. Start a deck: npm create @kadal/deck@latest my-talkpnpm create @kadal/deck my-talkyarn create @kadal/deck my-talkbun create @kadal/deck my-talk.
  2. Copy slides.md to my-talk/deck.mdx, and public/ across.
  3. Change the theme’s name if it differs (below). Every theme ships with the engine.
  4. Turn Slidev’s directives and Vue syntax into their Kadal spellings (below).
  5. Run npx kadal-deck checkpnpm exec kadal-deck checkyarn kadal-deck checkbunx kadal-deck check. It names, with file and line, everything that won’t compile or won’t do what it looks like, including Slidev syntax that compiles but does nothing (<div v-click>).

Slidev’s five official themes are Kadal packages, with the same layouts, layout names, frontmatter, slots, components, themeConfig keys and colour schemes. Only the name changes for two of them:

Slidev theme: Kadal theme: Its package, to pin a version (optional)
default classic @kadal/theme-classic
seriph seriph @kadal/theme-seriph
apple-basic crisp @kadal/theme-crisp
bricks bricks @kadal/theme-bricks
shibainu shibainu @kadal/theme-shibainu
none none built in

theme: default is not a Kadal theme: Kadal’s default is minimal, so write classic for Slidev’s look. The ports are MIT-licensed like the originals, and each package’s LICENSE keeps the Slidev team’s copyright notice. A community theme has no Kadal port: pick one from the gallery, or write one. On deck.kadal.cc every theme in the gallery is there without installing anything.

  • --- separates slides; the first YAML block is the headmatter; YAML after a separator is that slide’s frontmatter; a yaml code block first in a slide is its frontmatter too.
  • defaults:, src: ./part.md#2-5, hide, hideInToc, level, routeAlias, preload, zoom, clicks, clicksStart, transition, layout, class, background.
  • aspectRatio and canvasWidth set the stage size. (Kadal authors at 1920 × 1080; sizes in a Slidev theme are scaled for it.)
  • Notes in a comment at the end of a slide, [click] and [click:3] in them, notesAutoRuby.
  • ::right::, ::left::, ::default:: slots; two-cols-header and every other built-in layout name.
  • A <style> in a slide is scoped to it; <style global> is not.
  • Code: {2|3-5|all}, {lines:true,startLine:5,maxHeight:'200px'}, [file.ts], // [!code ++], ::code-group, <<< @/snippets/x.ts#region, twoslash, {monaco}, {monaco-diff}, {monaco-run}, {monaco-write}, and md magic-move blocks with highlight steps in their fences.
  • ```mermaid {theme: 'neutral'}, ```plantuml, $$ {1|3|all}, katex and mermaid settings, plantUmlServer.
  • mdc: true (Kadal’s name is comark: true) and its syntax. See Comark syntax.
  • fonts: { sans, serif, mono, weights, italic, provider, local }, themeConfig, addons, download, timer, duration, contextMenu, remoteAssets, seoMeta, favicon.
  • Icon tags such as <mdi-account /> and <carbon-logo-github />; v-drag, <v-drag> and <v-drag-arrow>.
  • global-top, global-bottom and custom-nav-controls layers, and pages of the deck’s own in setup/routes.ts (see Layers, controls and pages).
  • Export flags: --format, --range, --dark, --with-clicks (also --with-clicks false), --omit-background, --timeout, --wait, --executable-path, --per-slide, --output.

MDX is stricter than Slidev’s Markdown: attributes are JSX (className, style={{ color: "red" }}, numbers and arrays in braces), components start with a capital letter, every tag closes, and { and < in prose need backticks or escaping.

Slidev Kadal Deck
<div v-click>…</div>, <v-click> <Click>…</Click>
v-click="3", v-click="'+2'", v-click="[2, 4]" <Click at={3}>, <Click at="+2">, <Click at={[2, 4]}>
v-click.hide <Click hide>
v-after, <v-after> <After>
<v-clicks depth="2" every="2"> <Clicks depth={2} every={2}>
v-switch with #1, #2-4 slots <Switch> with <Case at={1}>, <Case at="2-4">
v-mark.circle.red <Mark type="circle" color="red">
v-motion :initial :enter :click-1 <Motion initial enter clicks={{ "click-1": … }}>
{{ $slidev.nav.currentPage }}, $page <SlideCurrentNo />; in a component, useSlide()
{{ $clicks }} <ClickCount />; in a component, useClicks()
colorSchema: dark colorScheme: dark
transition: slide-left transition: slide (also fade, zoom, convex, concave, your own)
components/*.vue React components in src/components.tsx, or a Vue island with @kadal/deck-vue
layouts/*.vue "layout:name": Component in src/components.tsx
global-top.vue, global-bottom.vue, custom-nav-controls.vue global-top.tsx, global-bottom.tsx, custom-nav-controls.tsx
setup/shortcuts.ts <Deck shortcuts> or keyboard: in the headmatter
setup/context-menu.ts <Deck contextMenu>
setup/code-runners.ts the same file, with defineCodeRunners from @kadal/deck
setup/routes.ts the same file (or routes.tsx), with defineRoutes from @kadal/deck
onSlideEnter, onSlideLeave the same names, or useSlideEnter, useSlideLeave
drawings: { persist: true } (files in .slidev/drawings) drawings: { persist: files } (files in drawings/; Slidev’s SVGs load)
UnoCSS classes Tailwind v4 in the starter: most utility names are the same
slidev, slidev build, slidev export, slidev format kadal-deck dev, kadal-deck build, kadal-deck export, kadal-deck format
Slidev Kadal Deck
<Arrow>, <Link>, <Toc>, <Transform>, <Tweet>, <Youtube> the same names
<AutoFitText> <FitText>
<VClick>, <VAfter>, <VClicks> <Click>, <After>, <Clicks>; see Clicks and animation
<VSwitch> <Switch> with <Case>s
v-mark <Mark>
<VDrag>, <VDragArrow>, the v-drag attribute <Drag>, <DragArrow>; see Components
<SlidevVideo> <Video>
v-motion <Motion>
<LightOrDark>, <RenderWhen>, <SlideCurrentNo>, <SlidesTotal>, <TitleRenderer> the same names
<carbon-logo-github /> and other icon tags the same tags, or <Icon name="carbon:logo-github" />; see Icons
<PoweredBySlidev> the host’s “Made with Kadal Deck” badge

In Comark syntax Slidev’s kebab-case names work too: :auto-fit-text[Big] is <FitText>.

Slidev features and their Kadal equivalents

Section titled “Slidev features and their Kadal equivalents”
Slidev feature In Kadal Deck
Presenter mode, timer, notes editing Presenter view, with pacing and a phone remote
Quick overview, /overview O, and /overview with step sliders and notes
Drawing (drauu) Drawing: pen, highlighter, shapes, eraser, undo
Camera view and recording Camera and recording
--remote, --tunnel kadal-deck dev --remote <password>, --tunnel; a hosted room with publish
Export to PDF, PNG, PPTX, Markdown Export, plus /export in the browser and an editable PPTX
build --download, download: the same; on deck.kadal.cc a paid-plan feature
build for a static host, PWA, remoteAssets Build and host anywhere
Icons (Iconify) Icons
Theme packages, theme eject Themes, Writing a theme
slidev format, Prettier plugin kadal-deck format, @kadal/prettier-plugin
Side editor, VS Code extension E under kadal-deck dev; the VS Code extension
Hosting a static build kadal-deck build, or kadal-deck publish to deck.kadal.cc

Decks that started life in reveal.js keep working too. These are synonyms, so you can paste them in and tidy up later:

  • <Fragment> is <Click>; <Stack>, <Stretch> and <Frame> are reveal.js’s r-stack, r-stretch and r-frame.
  • A code fence’s data-line-numbers="1|2-3" (with data-ln-start-from) means line numbers and highlight steps.
  • autoslide on a slide is autoSlide.
  • The slide keys backgroundColor, backgroundGradient, backgroundImage, backgroundSize, backgroundPosition, backgroundRepeat, backgroundOpacity, backgroundVideo, backgroundVideoLoop, backgroundVideoMuted, backgroundIframe and backgroundInteractive set the slide’s background.
  • Deck options keep reveal.js’s names: controls, progress, slideNumber, hash, history, loop, rtl, navigationMode, autoSlide, viewDistance and the rest of Configuration.

A live room where the audience follows, reacts, asks and votes from their phones; a phone remote with an air pointer, on a static host too; vertical slides; auto-animate; 36 animated backgrounds; a theme menu on every screen; Excalidraw sketches drawn in code; glossary term cards; narration; an editable PowerPoint export; framework islands for Vue, Svelte, Solid and more; kadal-deck check; and MCP servers for AI agents.

Send feedback