reveal.js v6.0.1 is a JavaScript presentation framework that transforms semantic HTML into fully interactive slide decks with transitions, fragments, speaker notes, code highlighting, math rendering, and more. This guide covers every architectural detail, configuration option, and operational concern an administrator or developer needs to deploy, extend, customize, and maintain reveal.js presentations at any scale.
For installation walkthroughs, everyday usage, and feature tutorials, see the User Guide.
1. Architecture Overview
1.1 Design Philosophy
reveal.js uses a closure-based factory pattern rather than ES6 classes. The main entry point (js/reveal.js) exports a factory function that returns a Reveal object. Each presentation instance is a self-contained closure with private state and a public API surface. This enables multiple independent presentations on a single page without shared mutable state.
The framework has zero runtime dependencies. Every dependency in package.json is a devDependency used only during build time. The distributed bundle is fully self-contained.
1.2 Entry Point and Initialization Flow
The initialization sequence proceeds through these stages:
- Factory invocation --
Reveal()ornew Reveal()creates a presentation instance - Config merge --
defaultConfigfromjs/config.tsis merged with constructor options - DOM validation -- locates the
.revealroot element (throws"Unable to find presentation root (<div class="reveal">)"if missing) - Controller instantiation -- all 20 internal controllers are created
- Plugin registration -- plugins from
config.pluginsarray are registered - Plugin initialization -- all plugins'
init()methods are called and awaited - Layout -- initial layout calculation and slide positioning
readyevent -- fires only after all plugins have initialized
Important
The
readyevent is the only safe point to begin interacting with the API programmatically. Code that runs beforereadymay encounter uninitialized plugins or incomplete DOM state.
1.3 Controller Inventory
Every controller is instantiated during initialization and manages a specific aspect of the presentation. Controllers communicate through the central Reveal instance and its event system.
| Controller | Source File | Responsibility |
|---|---|---|
| SlideContent | js/controllers/slidecontent.js |
Lazy-loading of slide content, media lifecycle (play/pause/stop), iframe management, preloading based on viewDistance |
| SlideNumber | js/controllers/slidenumber.js |
Slide number rendering in configurable formats (h.v, h/v, c, c/t) |
| JumpToSlide | js/controllers/jumptoslide.js |
Jump-to-slide overlay triggered by G key, accepts slide number or h.v format |
| Backgrounds | js/controllers/backgrounds.js |
Creates and synchronizes .slide-background elements for images, videos, iframes, gradients, and solid colors |
| AutoAnimate | js/controllers/autoanimate.js |
FLIP-based animation between consecutive data-auto-animate slides, element matching by data-id or DOM heuristics |
| ScrollView | js/controllers/scrollview.js |
Scroll-based view mode as alternative to slide-by-slide navigation |
| PrintView | js/controllers/printview.js |
PDF-optimized layout for print/export workflows |
| Fragments | js/controllers/fragments.js |
Fragment stepping, ordering, grouping, and state management |
| Overview | js/controllers/overview.js |
Bird's-eye view of all slides (activated by ESC or O key) |
| Keyboard | js/controllers/keyboard.js |
Keyboard shortcut registration, dispatch, and custom binding management |
| Location | js/controllers/location.js |
URL hash reading/writing, history API integration, deep linking |
| Controls | js/controllers/controls.js |
Navigation arrow rendering, tutorial animation, directional availability |
| Progress | js/controllers/progress.js |
Progress bar rendering and click-to-navigate |
| Pointer | js/controllers/pointer.js |
Cursor visibility management, auto-hide after hideCursorTime |
| Plugins | js/controllers/plugins.js |
Plugin registration, initialization sequencing, lifecycle management |
| Overlay | js/controllers/overlay.js |
Link preview overlays for iframes, images, and videos |
| Touch | js/controllers/touch.js |
Touch and swipe gesture recognition for mobile navigation |
| Focus | js/controllers/focus.js |
Focus state management and accessibility |
| Notes | js/controllers/notes.js |
Internal notes state tracking for <aside class="notes"> and data-notes |
| Playback | js/components/playback.js |
Canvas-based play/pause UI for auto-slide timer |
1.4 Module Dependency Graph
reveal.js (factory)
+-- config.ts (defaults + RevealConfig interface)
+-- controllers/ (20 controllers)
| +-- plugins.js --> plugin instances
| +-- slidecontent.js --> utils/loader.ts
| +-- autoanimate.js --> utils/util.ts
| +-- location.js --> utils/util.ts (getQueryHash)
| +-- backgrounds.js --> utils/color.ts
+-- utils/
| +-- color.ts (colorToRgb, colorBrightness)
| +-- constants.ts (selectors, blacklists, regex)
| +-- device.ts (isMobile, isChrome, isAndroid)
| +-- loader.ts (dynamic script/link injection)
| +-- util.ts (DOM helpers, math, serialization)
+-- components/
+-- playback.js (canvas UI)
1.5 Utility Modules
| Module | Exports | Purpose |
|---|---|---|
js/utils/color.ts |
colorToRgb(), colorBrightness() |
Parse CSS color strings and compute brightness for light/dark background detection |
js/utils/constants.ts |
SLIDES_SELECTOR, POST_MESSAGE_METHOD_BLACKLIST, FRAGMENT_STYLE_REGEX, slide number format constants |
Centralized selectors and security constants |
js/utils/device.ts |
isMobile, isChrome, isAndroid |
User-agent based device detection for touch/rendering behavior |
js/utils/loader.ts |
loadScript() |
Dynamic <script> injection with callback |
js/utils/util.ts |
extend(), queryAll(), toggleClass(), deserialize(), distanceBetween(), transformElement(), matches(), closest(), enterFullscreen(), createSingletonNode(), createStyleSheet(), getQueryHash(), getRemainingHeight(), getMimeTypeFromFile(), encodeRFC3986URI() |
DOM manipulation, math, serialization, fullscreen API |
2. System Requirements
2.1 Build Environment
| Requirement | Minimum Version |
|---|---|
| Node.js | >= 20.19.0 (enforced in package.json engines field) |
| npm | Bundled with Node.js 20+ |
2.2 Build Toolchain (devDependencies)
All dependencies are development-only. The built output has zero runtime dependencies.
| Package | Version | Role |
|---|---|---|
| TypeScript | ^6.0.2 | Type checking and declaration generation |
| Vite | ^8.0.3 | Build system and dev server |
| esbuild | ^0.28.0 | ES5 fallback transpilation |
| Sass | ^1.98.0 | SCSS theme compilation |
| vite-plugin-dts | ^4.5.4 | TypeScript declaration file generation |
| highlight.js | ^11.11.1 | Bundled into Highlight plugin |
| marked | ^17.0.5 | Bundled into Markdown plugin |
| marked-smartypants | ^1.1.11 | Smart quotes for Markdown plugin |
| fitty | ^2.4.2 | Text fitting utility |
| jszip | ^3.10.1 | ZIP archive creation for npm run package |
| glob | ^13.0.6 | File globbing for build scripts |
| QUnit | ^2.25.0 | Test framework |
| node-qunit-puppeteer | ^2.2.1 | Headless browser test runner |
2.3 Browser Support
reveal.js targets modern browsers with ES module support. The npm run build:es5 command produces an ES5 fallback bundle via esbuild for legacy environments.
| Browser | Support |
|---|---|
| Chrome/Edge | Current and recent versions |
| Firefox | Current and recent versions |
| Safari | Current and recent versions |
| Mobile Safari/Chrome | Supported with touch gestures |
| IE 11 | ES5 fallback only (reveal.es5.js) |
Note
Device detection in
js/utils/device.tsapplies browser-specific workarounds. TheisChromeandisAndroidflags affect touch handling and rendering behavior.
2.4 React Wrapper Requirements
The @revealjs/react package (v0.2.1) in react/ has these peer dependencies:
| Package | Version |
|---|---|
| react | >= 18 |
| react-dom | >= 18 |
| reveal.js | >= 5 |
3. Configuration Reference
3.1 Config Resolution Order
Configuration values are merged with increasing precedence:
defaultConfig-- hardcoded defaults injs/config.tsReveal.configure()calls beforeinitialize()-- pre-init overrides- Constructor options --
Reveal({ ... })ornew Reveal({ ... }) Reveal.initialize()options -- final programmatic overrides- URL query parameters -- parsed by
getQueryHash()from the URL fragment
Tip
URL query parameters provide an easy way to override config for testing or sharing. Append them after
#in the URL:presentation.html#/2?transition=none&controls=false. Values are automatically deserialized (strings"true"/"false"become booleans, numeric strings become numbers).
3.2 Complete Config Options
Every option below is from the defaultConfig object in js/config.ts and the RevealConfig interface.
3.2.1 Presentation Dimensions
| Option | Type | Default | Description |
|---|---|---|---|
width |
number |
960 |
Presentation width in pixels. The framework scales content to fit the viewport while maintaining this aspect ratio. |
height |
number |
700 |
Presentation height in pixels. |
margin |
number |
0.04 |
Factor of empty space around the content (0.0 = no margin, 0.5 = half the viewport). |
minScale |
number |
0.2 |
Minimum scale factor applied to the presentation. |
maxScale |
number |
2.0 |
Maximum scale factor applied to the presentation. |
3.2.2 Controls
| Option | Type | Default | Description |
|---|---|---|---|
controls |
boolean |
true |
Show navigation arrow controls. |
controlsTutorial |
boolean |
true |
Show a subtle bounce animation on controls the first time a presentation is viewed to hint at interactivity. |
controlsLayout |
string |
'bottom-right' |
Position of controls. Allowed values: 'bottom-right', 'edges'. |
controlsBackArrows |
string |
'faded' |
Visibility of back-direction arrows. Allowed values: 'faded', 'hidden', 'visible'. |
3.2.3 Progress and Slide Numbers
| Option | Type | Default | Description |
|---|---|---|---|
progress |
boolean |
true |
Show a thin progress bar at the bottom. |
slideNumber |
boolean | string |
false |
Display slide number. Set to true for default format, or a format string: 'h.v' (horizontal.vertical), 'h/v', 'c' (flattened count), 'c/t' (count/total). |
showSlideNumber |
string |
'all' |
Where slide numbers appear. Allowed values: 'all', 'print', 'speaker'. |
3.2.4 Navigation and History
| Option | Type | Default | Description |
|---|---|---|---|
hash |
boolean |
false |
Write the current slide hash to the URL, enabling browser back/forward navigation. |
hashOneBasedIndex |
boolean |
false |
Use 1-based indices in URL hash instead of 0-based. |
respondToHashChanges |
boolean |
true |
Listen for hashchange events and navigate accordingly. |
jumpToSlide |
boolean |
true |
Enable the jump-to-slide overlay (triggered by G key). |
history |
boolean |
false |
Push each slide change to the browser history (creates back/forward entries). |
keyboard |
boolean |
true |
Enable keyboard navigation shortcuts. |
keyboardCondition |
string | null | function |
null |
Condition for keyboard activation. Set to 'focused' to only enable when the presentation has focus. Can also be a function returning boolean. |
overview |
boolean |
true |
Enable the slide overview mode (ESC/O key). |
loop |
boolean |
false |
Loop the presentation (navigating past the last slide returns to the first). |
rtl |
boolean |
false |
Right-to-left navigation order. |
navigationMode |
string |
'default' |
Navigation mode. Allowed values: 'default', 'linear', 'grid'. Linear flattens all slides. Grid enables full 2D arrow navigation. |
shuffle |
boolean |
false |
Randomize slide order on each presentation load. |
3.2.5 Content Behavior
| Option | Type | Default | Description |
|---|---|---|---|
center |
boolean |
true |
Vertically center slide content. |
touch |
boolean |
true |
Enable touch/swipe navigation on touch devices. |
fragments |
boolean |
true |
Enable fragment stepping (sequential reveal of elements). |
fragmentInURL |
boolean |
true |
Include fragment index in the URL hash. |
help |
boolean |
true |
Enable the help overlay (? key). |
pause |
boolean |
true |
Enable pause mode (. or B key). |
showNotes |
boolean |
false |
Show speaker notes in the presentation view (visible to audience). |
showHiddenSlides |
boolean |
false |
Show slides marked with data-visibility="hidden". |
sortFragmentsOnSync |
boolean |
true |
Automatically sort fragments by their data-fragment-index on sync. |
3.2.6 Media and Embedding
| Option | Type | Default | Description |
|---|---|---|---|
autoPlayMedia |
boolean | null |
null |
Auto-play <video> and <audio> on slide entry. null = auto-play only elements with autoplay attribute. true = always. false = never. |
preloadIframes |
boolean | null |
null |
Pre-load <iframe> content. null = preload only elements with data-preload. true = always. false = never. |
previewLinks |
boolean |
false |
Open links in an iframe overlay instead of navigating away. |
viewDistance |
number |
3 |
Number of slides away from current to pre-load content for. |
mobileViewDistance |
number |
2 |
Same as viewDistance but for mobile devices (reduced for performance). |
3.2.7 Appearance
| Option | Type | Default | Description |
|---|---|---|---|
display |
string |
'block' |
CSS display value applied to each slide when shown. |
hideInactiveCursor |
boolean |
true |
Hide the cursor after a period of inactivity. |
hideCursorTime |
number |
5000 |
Milliseconds before an inactive cursor is hidden. |
embedded |
boolean |
false |
Mark the presentation as embedded within a larger page (affects sizing/layout). |
disableLayout |
boolean |
false |
Disable all reveal.js layout/scaling. Useful for fully custom CSS layouts. |
3.2.8 Auto-Animate
| Option | Type | Default | Description |
|---|---|---|---|
autoAnimate |
boolean |
true |
Enable auto-animate between consecutive slides with data-auto-animate. |
autoAnimateMatcher |
function | null |
null |
Custom element matching function for auto-animate. Signature: (fromEl, toEl) => boolean. |
autoAnimateEasing |
string |
'ease' |
Default CSS easing for auto-animate transitions. |
autoAnimateDuration |
number |
1.0 |
Default duration in seconds for auto-animate transitions. |
autoAnimateUnmatched |
boolean |
true |
Animate unmatched elements (fade in/out) during auto-animate. |
autoAnimateStyles |
string[] |
['opacity', 'color', 'background-color', 'padding', 'font-size', 'line-height', 'letter-spacing', 'border-width', 'border-color', 'border-radius', 'outline', 'outline-offset'] |
CSS properties that are auto-animated when they differ between matched elements. |
3.2.9 Auto-Slide
| Option | Type | Default | Description |
|---|---|---|---|
autoSlide |
number |
0 |
Milliseconds between automatic slide transitions. 0 disables. |
autoSlideStoppable |
boolean |
true |
Allow the user to stop auto-sliding by interacting with the presentation. |
autoSlideMethod |
function | null |
null |
Custom function called to advance slides during auto-slide. Receives the Reveal instance. |
defaultTiming |
number | null |
null |
Default timing for slides without data-autoslide. If set, used instead of autoSlide per slide. |
3.2.10 Transitions
| Option | Type | Default | Description |
|---|---|---|---|
transition |
string |
'slide' |
Default slide transition. Allowed values: 'none', 'fade', 'slide', 'convex', 'concave', 'zoom'. |
transitionSpeed |
string |
'default' |
Transition speed. Allowed values: 'default', 'fast', 'slow'. |
backgroundTransition |
string |
'fade' |
Background transition style. Same allowed values as transition. |
3.2.11 Parallax Backgrounds
| Option | Type | Default | Description |
|---|---|---|---|
parallaxBackgroundImage |
string |
'' |
URL of parallax background image. |
parallaxBackgroundSize |
string |
'' |
CSS background-size for parallax image (e.g., '2100px 900px'). |
parallaxBackgroundRepeat |
string |
'' |
CSS background-repeat for parallax image. |
parallaxBackgroundPosition |
string |
'' |
CSS background-position for parallax image. |
parallaxBackgroundHorizontal |
number | null |
null |
Pixels to shift parallax background per slide horizontally. Auto-calculated if null. |
parallaxBackgroundVertical |
number | null |
null |
Pixels to shift parallax background per slide vertically. Auto-calculated if null. |
3.2.12 Scroll View
| Option | Type | Default | Description |
|---|---|---|---|
view |
string | null |
null |
View mode. Set to 'scroll' for scroll-based presentation. null for default slide view. |
scrollLayout |
string |
'full' |
Scroll view layout. Allowed values: 'full', 'compact'. |
scrollSnap |
string |
'mandatory' |
CSS scroll-snap behavior. Allowed values: 'mandatory', 'proximity'. |
scrollProgress |
string |
'auto' |
Show scroll progress indicator. Allowed values: 'auto', true, false. |
scrollActivationWidth |
number |
435 |
Viewport width threshold (in pixels) below which scroll view is automatically activated. |
3.2.13 PDF Export
| Option | Type | Default | Description |
|---|---|---|---|
pdfMaxPagesPerSlide |
number |
Infinity |
Maximum number of PDF pages generated per slide (for very tall slides). Infinity means no limit. |
pdfSeparateFragments |
boolean |
true |
Give each fragment step its own page in the PDF. |
pdfPageHeightOffset |
number |
-1 |
Offset applied to PDF page height to avoid page-break issues. -1 uses auto-detection. |
3.2.14 Plugins and PostMessage
| Option | Type | Default | Description |
|---|---|---|---|
plugins |
array |
[] |
Array of plugin instances or factory functions to register. |
dependencies |
array |
[] |
Legacy dependency loading (deprecated in favor of plugins). |
postMessage |
boolean |
true |
Enable the postMessage API for cross-window communication. |
postMessageEvents |
boolean |
false |
Dispatch all reveal.js events as postMessage messages to the parent window. |
focusBodyOnPageVisibilityChange |
boolean |
true |
Focus document.body when the page becomes visible (prevents focus loss). |
preventIframeAutoFocus |
boolean |
false |
Prevent iframe embeds from automatically stealing focus. |
3.3 Plugin-Specific Config Keys
Plugin configuration is nested under the main config object using the plugin name as the key.
3.3.1 Highlight Plugin Config
Reveal.initialize({
highlight: {
highlightOnLoad: true, // boolean - Auto-highlight <pre><code> on load
escapeHTML: true, // boolean - Escape HTML entities in code blocks
beforeHighlight: null // function(code, block) - Callback before highlighting
}
});
3.3.2 Markdown Plugin Config
Reveal.initialize({
markdown: {
separator: '\\r?\\n---\\r?\\n', // string - Horizontal slide separator regex
verticalSeparator: null, // string|null - Vertical slide separator regex
notesSeparator: '^\\s*notes?:', // string - Notes separator regex
animateLists: false, // boolean - Wrap <li> elements in fragments
smartypants: false, // boolean - Enable typographic quote replacement
// All marked library options are also accepted:
gfm: true, // boolean - GitHub Flavored Markdown
breaks: false, // boolean - Treat newlines as <br>
renderer: null // object - Custom marked renderer
}
});
3.3.3 Math Plugin Config (KaTeX)
Reveal.initialize({
katex: {
local: null, // string|null - Path to local KaTeX installation
version: 'latest', // string - KaTeX version from CDN
delimiters: [ // array - Delimiter pairs for math detection
{ left: '$$', right: '$$', display: true },
{ left: '$', right: '$', display: false },
{ left: '\\(', right: '\\)', display: false },
{ left: '\\[', right: '\\]', display: true }
],
ignoredTags: ['script', 'noscript', 'style', 'textarea', 'pre'],
extensions: [] // array - KaTeX extensions (e.g., ['mhchem'])
}
});
CDN base: cdn.jsdelivr.net/npm/katex
3.3.4 Math Plugin Config (MathJax2)
Reveal.initialize({
mathjax2: {
mathjax: 'https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js',
config: 'TeX-AMS_HTML-full', // string - MathJax configuration preset
tex2jax: {
inlineMath: [['$', '$'], ['\\(', '\\)']],
skipTags: ['script', 'noscript', 'style', 'textarea', 'pre']
}
}
});
MathJax2 retypesets on every slidechanged event.
3.3.5 Math Plugin Config (MathJax3)
Reveal.initialize({
mathjax3: {
mathjax: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js',
tex: {
inlineMath: [['$', '$'], ['\\(', '\\)']]
},
options: {
skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre']
},
startup: {
ready: null // function - Custom startup handler
}
}
});
Sets window.MathJax configuration object before loading the library.
3.3.6 Math Plugin Config (MathJax4)
Reveal.initialize({
mathjax4: {
mathjax: 'https://cdn.jsdelivr.net/npm/mathjax@4/tex-mml-chtml.js',
tex: {
inlineMath: [['$', '$'], ['\\(', '\\)']]
},
options: {
skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre']
},
startup: {
ready: null
},
output: 'chtml' // string - Output format
}
});
Uses async startup and waits for MathJax.startup.promise to resolve.
4. Deployment & Scaling
4.1 Build Commands
| Command | Purpose | Output |
|---|---|---|
npm run dev / npm start |
Start Vite dev server with hot reload | Serves from source |
npm run build |
Full production build (TypeScript + Vite core + styles + all 6 plugins) | dist/ |
npm run build:core |
Build core library only | dist/reveal.js, dist/reveal.mjs, dist/reveal.d.ts |
npm run build:es5 |
ES5 fallback bundle via esbuild | dist/reveal.es5.js |
npm run build:styles |
Compile SCSS themes and base styles | dist/reveal.css, dist/reset.css, dist/theme/*.css |
npm run package |
Create distributable ZIP archive | ZIP file via jszip |
npm test |
Run QUnit tests in headless Puppeteer | Console output |
npm run react:build |
Build React wrapper | react/dist/ |
npm run react:demo |
Run React wrapper demo | Dev server |
npm run react:test |
Run React wrapper tests (Vitest + Testing Library) | Console output |
4.2 Build Architecture
Each plugin has its own vite.config.ts that produces both ES module (.mjs) and UMD (.js) formats. TypeScript declarations are generated via vite-plugin-dts with a custom path rewriting plugin at plugin/vite-plugin-dts.ts.
The build pipeline:
TypeScript compile (tsc)
+-- Vite build: core (js/index.ts -> dist/reveal.{js,mjs})
+-- Vite build: highlight plugin (-> dist/plugin/highlight.{js,mjs})
+-- Vite build: markdown plugin (-> dist/plugin/markdown.{js,mjs})
+-- Vite build: math plugin (-> dist/plugin/math.{js,mjs})
+-- Vite build: notes plugin (-> dist/plugin/notes.{js,mjs})
+-- Vite build: search plugin (-> dist/plugin/search.{js,mjs})
+-- Vite build: zoom plugin (-> dist/plugin/zoom.{js,mjs})
+-- Sass compile: themes (css/theme/*.scss -> dist/theme/*.css)
+-- Sass compile: base (css/reveal.scss -> dist/reveal.css)
+-- Copy: reset.css -> dist/reset.css
4.3 Package Exports Map
The package.json exports field defines the public module entry points:
| Specifier | CJS | ESM | Types |
|---|---|---|---|
. |
dist/reveal.js |
dist/reveal.mjs |
dist/reveal.d.ts |
./reveal.css |
dist/reveal.css |
-- | -- |
./reset.css |
dist/reset.css |
-- | -- |
./theme/* |
dist/theme/* |
-- | -- |
./plugin/highlight |
dist/plugin/highlight.js |
dist/plugin/highlight.mjs |
dist/plugin/highlight.d.ts |
./plugin/markdown |
dist/plugin/markdown.js |
dist/plugin/markdown.mjs |
dist/plugin/markdown.d.ts |
./plugin/math |
dist/plugin/math.js |
dist/plugin/math.mjs |
dist/plugin/math.d.ts |
./plugin/notes |
dist/plugin/notes.js |
dist/plugin/notes.mjs |
dist/plugin/notes.d.ts |
./plugin/search |
dist/plugin/search.js |
dist/plugin/search.mjs |
dist/plugin/search.d.ts |
./plugin/zoom |
dist/plugin/zoom.js |
dist/plugin/zoom.mjs |
dist/plugin/zoom.d.ts |
4.4 Deployment Patterns
Static File Hosting
The simplest deployment is serving the dist/ directory (plus your HTML files) from any static host (Nginx, Apache, S3, Netlify, Vercel, GitHub Pages).
your-presentation/
index.html
dist/
reveal.js
reveal.mjs
reveal.css
reset.css
theme/
plugin/
CDN Usage
Warning
As of v6.0.1, CDN availability is a known issue (see GitHub issue #3894). Verify the version is available on your CDN of choice before relying on it for production.
npm Package
import Reveal from 'reveal.js';
import 'reveal.js/reveal.css';
import 'reveal.js/theme/black.css';
import Highlight from 'reveal.js/plugin/highlight';
import Markdown from 'reveal.js/plugin/markdown';
Self-Hosted with Vite Dev Server
For development, npm run dev starts Vite with hot module replacement. The index.html at the project root serves as the development entry point.
4.5 Multiple Instances
reveal.js supports multiple independent presentation instances on a single page. Each Reveal() call creates a new instance targeting a different .reveal root element:
let deck1 = new Reveal(document.querySelector('#deck1'), { embedded: true });
let deck2 = new Reveal(document.querySelector('#deck2'), { embedded: true });
await deck1.initialize();
await deck2.initialize();
Set embedded: true to prevent instances from capturing global keyboard events.
Caution
When running multiple instances, each must target a unique DOM element. Initializing two instances on the same
.revealroot leads to undefined behavior. Theembedded: trueflag is essential to prevent keyboard/focus conflicts.
5. Operations
5.1 Theme System
reveal.js ships 14 themes, all authored as SCSS in css/theme/:
| Theme | File | Character |
|---|---|---|
| beige | beige.scss |
Warm, parchment-like |
| black | black.scss |
Dark, high contrast (default) |
| black-contrast | black-contrast.scss |
WCAG-enhanced dark |
| blood | blood.scss |
Dark red dramatic |
| dracula | dracula.scss |
Dracula color scheme |
| league | league.scss |
Dark gray textured |
| moon | moon.scss |
Dark blue/gray |
| night | night.scss |
Pure dark |
| serif | serif.scss |
Light, serif typography |
| simple | simple.scss |
Minimal light |
| sky | sky.scss |
Light blue |
| solarized | solarized.scss |
Solarized light palette |
| white | white.scss |
Clean white |
| white-contrast | white-contrast.scss |
WCAG-enhanced light |
Theme Architecture
The theme template system uses three files in css/theme/template/:
settings.scss-- Defines all SCSS variables (fonts, colors, spacing, heading sizes)mixins.scss-- Shared SCSS mixinstheme.scss-- Base theme styles that consume the variables
Each theme file imports the template, overrides variables, and optionally adds custom rules. To create a custom theme:
// my-theme.scss
@use 'template/settings' with (
$backgroundColor: #191919,
$mainColor: #fff,
$headingColor: #fff,
$linkColor: #42affa
);
@use 'template/theme';
5.2 PDF Export
PDF export works through the PrintView controller. Access the print-optimized layout by appending ?print-pdf to the URL, then use the browser's print dialog (Ctrl/Cmd+P).
Key config options:
pdfSeparateFragments: true-- each fragment step gets its own pagepdfMaxPagesPerSlide: Infinity-- limit pages for very tall slidespdfPageHeightOffset: -1-- auto-detect page height offset
The pdf-ready event fires when the print layout is fully rendered and ready for capture.
Note
GitHub issue #3860 reports that PDF export can generate very large files. Issue #3888 documents compatibility problems with Decktape (a popular third-party PDF export tool). For reliable PDF export, use the browser's native print dialog with Chrome.
5.3 Keyboard Shortcuts
Default key bindings registered by the Keyboard controller:
| Key | Action |
|---|---|
| Right / Down / Space / N / Page Down | Next slide |
| Left / Up / Shift+Space / P / Page Up | Previous slide |
| Home | First slide |
| End | Last slide |
| F | Toggle fullscreen |
| ESC / O | Toggle overview |
| S | Open speaker notes window |
| G | Jump to slide overlay |
| ? | Toggle help overlay |
| . / B | Toggle pause (blackout) |
| V | Toggle black screen |
5.4 Events Reference
All events are dispatched via the reveal.js event system and can be listened to with Reveal.on():
| Event | Payload | Fires When |
|---|---|---|
ready |
{ indexh, indexv, currentSlide } |
Initialization complete, all plugins loaded |
slidechanged |
{ indexh, indexv, previousSlide, currentSlide, origin } |
Slide navigation occurs |
slidesync |
{ indexh, indexv, currentSlide } |
Slide content synchronized |
fragmentshown |
{ fragment, fragments } |
Fragment becomes visible |
fragmenthidden |
{ fragment, fragments } |
Fragment becomes hidden |
overviewshown |
{} |
Overview mode activated |
overviewhidden |
{} |
Overview mode deactivated |
paused |
{} |
Presentation paused |
resumed |
{} |
Presentation resumed |
autoanimate |
{ fromSlide, toSlide } |
Auto-animate transition occurs |
pdf-ready |
{} |
Print/PDF layout fully rendered |
previewiframe |
{ url } |
Iframe preview overlay shown |
previewimage |
{ url } |
Image preview overlay shown |
previewvideo |
{ url } |
Video preview overlay shown |
closeoverlay |
{} |
Preview overlay closed |
6. Performance
6.1 Lazy Loading
The SlideContent controller implements lazy loading based on viewDistance (default: 3 slides) and mobileViewDistance (default: 2). Content outside the view distance is not loaded until the user navigates near it.
This affects:
<img>withdata-src-- source set on approach, cleared on departure<video>and<audio>withdata-src-- source set on approach<iframe>withdata-src-- source set on approach, cleared on departure- Background images, videos, and iframes follow the same pattern
6.2 Layout Performance
layout()recalculates all slide positions and scaling. Called automatically on window resize.sync()re-processes all slides (content, backgrounds, fragments). Expensive; usesyncSlide()for single-slide updates.disableLayout: trueskips all reveal.js layout calculations, deferring entirely to CSS.- Background brightness detection (
colorBrightness()inutils/color.ts) addshas-light-backgroundorhas-dark-backgroundclasses, enabling automatic text color adaptation.
6.3 Mobile Optimization
mobileViewDistancereduces preloading on mobile to conserve bandwidth and memory- Touch controller uses passive event listeners where supported
- Device detection (
utils/device.ts) adjusts behavior for mobile browsers scrollActivationWidth: 435auto-activates scroll view on narrow viewports
Tip
For bandwidth-constrained deployments, reduce
viewDistanceandmobileViewDistanceto 1. This significantly reduces upfront resource loading at the cost of brief loading delays during navigation.
7. Security
7.1 PostMessage API Security
The postMessage API enables cross-window communication (used by the speaker notes window and embedded presentations). Security measures:
Method Blacklist (POST_MESSAGE_METHOD_BLACKLIST in js/utils/constants.ts):
The following methods are blocked from postMessage invocation to prevent unauthorized plugin injection, event hijacking, and preview abuse:
registerPluginregisterKeyboardShortcutaddKeyBindingaddEventListenershowPreviewpreviewIframe
Any postMessage attempting to call these methods is silently ignored.
7.2 Speaker Notes Window Security
The Notes plugin implements same-origin verification on postMessage events. Only messages from the same origin (window.location.origin) are accepted. This XSS safeguard prevents malicious cross-origin pages from injecting commands into the speaker view.
The notes window uses a heartbeat mechanism for orphaned window reconnection -- if the parent window closes and reopens, the existing notes window can re-establish the connection.
7.3 Query Parameter Sanitization
The getQueryHash() function in js/utils/util.ts explicitly deletes the dependencies key from parsed URL query parameters. This prevents script injection via crafted URLs that could load arbitrary remote scripts through the legacy dependency system.
7.4 Markdown External File Loading
External markdown files loaded via <section data-markdown="file.md"> use XHR requests, which are subject to the browser's same-origin policy. Cross-origin markdown files require appropriate CORS headers on the serving host.
Warning
When
postMessage: true(the default), any page that can reference your presentation's window can send navigation commands. SetpostMessage: falsefor presentations containing sensitive content that should not be externally controllable.
7.5 Content Security Policy Considerations
reveal.js dynamically creates <style> elements (via createStyleSheet() in utils/util.ts) and injects inline styles for layout. A strict CSP policy requires:
style-src 'unsafe-inline'-- for dynamic style injectionscript-srcmust allow the reveal.js bundle and any plugin scriptsframe-srcas needed for iframe backgrounds and preview overlays- Math plugins load scripts from
cdn.jsdelivr.netby default; either allow this origin or use thelocalconfig option
8. Developer Internals
8.1 Plugin Architecture
Plugin Interface
interface RevealPlugin {
id: string; // Unique identifier
init?(reveal: RevealApi): void | Promise<void>; // Called during initialization
destroy?(): void; // Called on Reveal.destroy()
}
Plugins can also be registered as factory functions that return a RevealPlugin object:
// Factory pattern
const MyPlugin = () => ({
id: 'my-plugin',
init: (reveal) => {
// Access full API
reveal.on('slidechanged', handleSlideChange);
reveal.getConfig(); // read config
},
destroy: () => {
// Cleanup
}
});
Reveal.initialize({
plugins: [MyPlugin]
});
Plugin Lifecycle
- Registration -- plugins are registered from
config.pluginsarray or viaReveal.registerPlugin(plugin) - Initialization --
Pluginscontroller calls each plugin'sinit()method, passing theRevealApiinstance - Async support -- if
init()returns aPromise, the framework waits for all plugin promises to resolve before firingready - Access --
Reveal.hasPlugin(id),Reveal.getPlugin(id),Reveal.getPlugins()for runtime inspection - Destruction --
Reveal.destroy()calls each plugin'sdestroy()method
Built-in Plugin Details
Highlight (id: 'highlight')
- Wraps highlight.js for syntax highlighting
- Processes all
<pre><code>blocks in the presentation data-line-numbersattribute format:"1,2|3,5-10"where|separates animation steps,,separates individual lines,-denotes rangesdata-trimnormalizes leading/trailing whitespacedata-noescapeprevents HTML entity escapingdata-fragment-indexcontrols fragment ordering when line highlighting steps
Markdown (id: 'markdown')
- Wraps
markedlibrary withmarked-smartypantsextension - Two authoring modes:
- Inline:
<section data-markdown>with<script type="text/template">or<textarea data-template> - External:
<section data-markdown="file.md">loads via XHR
- Inline:
- Attribute injection via HTML comments:
- Element:
<!-- .element: class="fragment" data-fragment-index="1" --> - Slide:
<!-- .slide: data-background="#ff0000" -->
- Element:
- Default separators: horizontal
\r?\n---\r?\n, verticalnull, notes^\s*notes?:
Math (wrapper, id depends on sub-plugin)
- Contains 4 sub-plugins:
KaTeX(id:'katex'),MathJax2(id:'mathjax2'),MathJax3(id:'mathjax3'),MathJax4(id:'mathjax4') - MathJax2 is the default if
Mathis registered without specifying a sub-plugin - Each sub-plugin loads its library from CDN by default but supports local paths
Notes (id: 'notes')
- Opens speaker view in a popup window (triggered by
Skey, keyCode 83) - Communication via
postMessagewith same-origin check - Notes sources (in priority order): fragment-level notes override slide-level
<aside class="notes">elementsdata-notesattribute on<section>
- Heartbeat for orphaned window reconnection
Search (id: 'search')
- Registers
Ctrl+Shift+Fkeyboard shortcut - Uses Hilitor algorithm for DOM-based text highlighting
- Highlight colors cycle:
#ff6,#a0ffff,#9f9,#f99,#f6f - Methods:
open(),close(),toggle() - Input field appears top-right, Enter advances to next match
Zoom (id: 'zoom')
- Alt+Click to zoom (Ctrl+Click on Linux)
- Config:
zoomKey(override modifier key),zoomLevel(default: 2) - Escape to zoom out
- CSS transform-based with mouse-tracking pan support
- Methods:
zoom.to(),zoom.out(),zoom.magnify(),zoom.reset(),zoom.zoomLevel()
8.2 Public API Reference
The complete public API as defined in reveal.d.ts:
Initialization and Lifecycle
| Method | Signature | Description |
|---|---|---|
initialize |
(options?: RevealOptions) => Promise<RevealApi> |
Initialize the presentation. Returns a promise that resolves when ready. |
configure |
(options: RevealOptions) => void |
Update configuration at runtime. Triggers re-layout. |
destroy |
() => void |
Destroy the instance, remove event listeners, clean up DOM. |
sync |
() => void |
Re-process all slides (content, backgrounds, fragments, notes). |
syncSlide |
(slide?: HTMLElement) => void |
Re-process a single slide. |
syncFragments |
(slide?: HTMLElement) => void |
Re-process fragments on a slide. |
Navigation
| Method | Signature | Description |
|---|---|---|
slide |
(h: number, v?: number, f?: number) => void |
Navigate to specific slide by indices (horizontal, vertical, fragment). |
left |
() => void |
Navigate left. |
right |
() => void |
Navigate right. |
up |
() => void |
Navigate up. |
down |
() => void |
Navigate down. |
prev |
() => void |
Navigate to previous slide. |
next |
() => void |
Navigate to next slide. |
prevFragment |
() => boolean |
Step to previous fragment. Returns false if no previous fragment. |
nextFragment |
() => boolean |
Step to next fragment. Returns false if no next fragment. |
navigateFragment |
(index: number) => void |
Navigate to a specific fragment index. |
shuffle |
() => void |
Randomize slide order. |
State
| Method | Signature | Description |
|---|---|---|
getState |
() => RevealState |
Get current presentation state (indices, paused, overview). |
setState |
(state: RevealState) => void |
Restore a previously captured state. |
getProgress |
() => number |
Get progress through presentation (0.0 to 1.0). |
getIndices |
(slide?: HTMLElement) => { h: number, v: number, f: number } |
Get indices of current or specified slide. |
getScale |
() => number |
Get current scale factor. |
getConfig |
() => RevealOptions |
Get active configuration. |
Slide Access
| Method | Signature | Description |
|---|---|---|
getSlides |
() => HTMLElement[] |
Get all slide elements as a flat array. |
getSlide |
(x: number, y?: number) => HTMLElement |
Get slide element at given indices. |
getCurrentSlide |
() => HTMLElement |
Get current slide element. |
getPreviousSlide |
() => HTMLElement |
Get previous slide element. |
getTotalSlides |
() => number |
Get total number of slides. |
getSlidePastCount |
() => number |
Get number of slides before current. |
getSlideNotes |
(slide?: HTMLElement) => string |
Get notes for current or specified slide. |
getSlideBackground |
(x: number, y?: number) => HTMLElement |
Get background element for slide at given indices. |
Queries
| Method | Signature | Description |
|---|---|---|
isReady |
() => boolean |
True after initialization completes. |
isPaused |
() => boolean |
True if presentation is paused. |
isOverview |
() => boolean |
True if overview mode is active. |
isFirstSlide |
() => boolean |
True if on the first slide. |
isLastSlide |
() => boolean |
True if on the last slide. |
availableRoutes |
() => { left: boolean, right: boolean, up: boolean, down: boolean } |
Check which navigation directions are available. |
availableFragments |
() => { prev: boolean, next: boolean } |
Check if previous/next fragments exist. |
Toggles
| Method | Signature | Description |
|---|---|---|
toggleHelp |
(override?: boolean) => void |
Toggle or set help overlay visibility. |
toggleOverview |
(override?: boolean) => void |
Toggle or set overview mode. |
togglePause |
(override?: boolean) => void |
Toggle or set pause state. |
toggleAutoSlide |
(override?: boolean) => void |
Toggle or set auto-slide. |
layout |
() => void |
Force a layout recalculation. |
Plugins
| Method | Signature | Description |
|---|---|---|
registerPlugin |
(plugin: RevealPlugin) => void |
Register a plugin at runtime. |
hasPlugin |
(id: string) => boolean |
Check if a plugin is registered. |
getPlugin |
(id: string) => RevealPlugin |
Get a registered plugin by ID. |
getPlugins |
() => { [id: string]: RevealPlugin } |
Get all registered plugins. |
Keyboard
| Method | Signature | Description |
|---|---|---|
addKeyBinding |
(binding: object, callback: function) => void |
Register a custom key binding. |
removeKeyBinding |
(keyCode: number) => void |
Remove a key binding. |
triggerKey |
(keyCode: number) => void |
Programmatically trigger a key press. |
registerKeyboardShortcut |
(key: string, description: string) => void |
Register a shortcut for the help overlay. |
Events
| Method | Signature | Description |
|---|---|---|
on |
(type: string, listener: function, options?: object) => void |
Add event listener. |
off |
(type: string, listener: function, options?: object) => void |
Remove event listener. |
dispatchEvent |
(event: Event) => void |
Dispatch a custom event. |
Preview
| Method | Signature | Description |
|---|---|---|
showPreview |
(url: string) => void |
Show a URL in the preview overlay. |
hidePreview |
() => void |
Hide the preview overlay. |
8.3 Data Attributes Reference
Slide-Level Attributes
| Attribute | Values | Description |
|---|---|---|
data-transition |
none, fade, slide, convex, concave, zoom |
Per-slide transition override. Supports directional: slide-in fade-out. |
data-transition-speed |
default, fast, slow |
Per-slide transition speed. |
data-autoslide |
integer (ms) | Per-slide auto-advance timing. |
data-notes |
string | Speaker notes for the slide. |
data-visibility |
hidden, uncounted |
Hide slide from navigation (hidden) or exclude from count (uncounted). |
data-state |
string | Adds a class to the <body> when the slide is active. |
data-preload |
(presence) | Force preloading of slide content regardless of viewDistance. |
Background Attributes
| Attribute | Values | Description |
|---|---|---|
data-background |
color/URL/gradient | Shorthand for background (auto-detects type). |
data-background-image |
URL | Background image URL. |
data-background-video |
URL | Background video URL. |
data-background-iframe |
URL | Background iframe URL. |
data-background-color |
CSS color | Background solid color. |
data-background-gradient |
CSS gradient | Background gradient (e.g., linear-gradient(...)). |
data-background-size |
CSS size | Background image size (e.g., cover, contain). |
data-background-position |
CSS position | Background image position. |
data-background-repeat |
CSS repeat | Background image repeat mode. |
data-background-opacity |
0.0-1.0 | Background opacity. |
data-background-transition |
transition name | Per-slide background transition. |
data-background-video-loop |
(presence) | Loop background video. |
data-background-video-muted |
(presence) | Mute background video. |
data-background-interactive |
(presence) | Allow interaction with background iframe. |
Auto-Animate Attributes
| Attribute | Values | Description |
|---|---|---|
data-auto-animate |
(presence) | Enable auto-animate for this slide. |
data-auto-animate-id |
string | Group auto-animate slides by ID (allows non-consecutive matching). |
data-auto-animate-restart |
(presence) | Start a new auto-animate sequence (no transition from previous). |
data-auto-animate-easing |
CSS easing | Per-slide easing override. |
data-auto-animate-duration |
number (seconds) | Per-slide duration override. |
data-auto-animate-delay |
number (seconds) | Per-element delay. |
data-auto-animate-unmatched |
fade, none |
Behavior for unmatched elements. |
data-id |
string | Explicit element matching ID for auto-animate. |
Fragment Animations
Applied as CSS classes on elements with class="fragment":
| Animation Class | Effect |
|---|---|
fade-out |
Fades element out |
fade-up |
Slides up while fading in |
fade-down |
Slides down while fading in |
fade-left |
Slides left while fading in |
fade-right |
Slides right while fading in |
fade-in-then-out |
Fades in, then fades out on next step |
fade-in-then-semi-out |
Fades in, then semi-transparent on next step |
grow |
Scales up from smaller |
shrink |
Scales down from larger |
strike |
Applies strikethrough |
highlight-red |
Highlights text red |
highlight-blue |
Highlights text blue |
highlight-green |
Highlights text green |
highlight-current-red |
Highlights red only while current |
highlight-current-blue |
Highlights blue only while current |
highlight-current-green |
Highlights green only while current |
Custom animation classes are also supported -- any string value works and is applied as a CSS class.
Tip
Fragment order is controlled by
data-fragment-index. Multiple elements can share the same index to animate simultaneously. WhensortFragmentsOnSyncistrue(default), fragments are automatically sorted by their index.
8.4 Error Messages
| Error | Source | Cause |
|---|---|---|
"Unable to find presentation root (<div class="reveal">)" |
js/index.ts:44 |
Reveal.initialize() called without a .reveal element in the DOM (or the specified root element). |
"Please specify a valid media type to preview" |
js/controllers/overlay.js:189 |
showPreview() called with a URL whose MIME type cannot be determined as image, video, or iframe. |
8.5 Test Infrastructure
| Framework | Scope | Location |
|---|---|---|
| QUnit + Puppeteer | Core library integration tests | test/ directory |
| Vitest + Testing Library + jsdom | React wrapper unit tests | react/ directory |
| TypeScript type tests | Type declaration correctness | test/types/plugin-imports.ts |
Test HTML pages in test/ cover: auto-animate, dependencies, grid navigation, iframes, markdown, mathjax4, multiple instances, PDF, plugins, scroll, and state.
Run tests:
npm test # Core: QUnit in headless Puppeteer
npm run react:test # React wrapper: Vitest
9. Integration Points
9.1 React Wrapper (@revealjs/react v0.2.1)
The React wrapper lives in the react/ subdirectory and provides a declarative component API for building presentations in React.
Components
Deck -- The root component that wraps a reveal.js instance.
| Prop | Type | Description |
|---|---|---|
config |
RevealOptions |
reveal.js configuration object |
plugins |
RevealPlugin[] |
Array of plugins to register |
onReady |
(event) => void |
Fires when presentation is initialized |
onSlideChange |
(event) => void |
Fires on slide navigation |
onFragmentShown |
(event) => void |
Fires when a fragment becomes visible |
onFragmentHidden |
(event) => void |
Fires when a fragment is hidden |
deckRef |
React.Ref |
Ref to the underlying Reveal instance |
Slide -- Maps to a <section> element with shorthand props for all data-* attributes.
| Prop | Type | Maps To |
|---|---|---|
background |
string |
data-background |
autoAnimate |
boolean |
data-auto-animate |
transition |
string |
data-transition |
notes |
string |
data-notes |
visibility |
string |
data-visibility |
state |
string |
data-state |
Stack -- Wraps vertical slides in a parent <section>.
Markdown -- Renders markdown content as slides.
| Prop | Type | Description |
|---|---|---|
children |
string |
Inline markdown content |
src |
string |
External markdown file URL |
separator |
string |
Horizontal slide separator |
verticalSeparator |
string |
Vertical slide separator |
notesSeparator |
string |
Notes separator |
options |
object |
Passed to marked library |
Fragment -- Wraps elements in fragment animations.
| Prop | Type | Description |
|---|---|---|
animation |
string |
Fragment animation class (e.g., 'fade-up') |
index |
number |
Fragment ordering index |
as |
string | Component |
Render as a different element type |
asChild |
boolean |
Polymorphic rendering (passes props to child) |
Code -- Renders syntax-highlighted code blocks.
| Prop | Type | Description |
|---|---|---|
language |
string |
highlight.js language identifier |
lineNumbers |
string |
Line number / step-through spec |
startFrom |
number |
Starting line number |
trim |
boolean |
Enable whitespace trimming |
noEscape |
boolean |
Disable HTML escaping |
Hooks and Context
useReveal()-- Hook that returns theRevealApiinstance fromRevealContext. Must be called within aDeckcomponent tree.RevealContext-- React context providing theRevealApiinstance.
Note
GitHub issue #3883 documents that React Markdown fragment directives are broken with Vite 8. Verify fragment behavior when using the Markdown component with the current build toolchain.
9.2 PostMessage API
When postMessage: true, reveal.js accepts commands from parent/child windows:
// From parent window controlling an embedded presentation
iframe.contentWindow.postMessage(JSON.stringify({
method: 'slide',
args: [2, 0] // Navigate to slide 2
}), '*');
When postMessageEvents: true, all reveal.js events are forwarded as postMessage to the parent window, enabling external monitoring and synchronization.
9.3 Multiplexing and Synchronization
State synchronization between presenter and audience can be built using:
getState()/setState()-- serialize and restore complete presentation statepostMessageEvents: true-- forward all events to a parent frame or relay serverslidechangedevent -- includesindexh,indexvfor remote navigation commands
9.4 Embedding
Set embedded: true for presentations embedded within larger pages:
- Prevents capturing global keyboard events
- Adjusts sizing behavior to fit within a container rather than the viewport
- Multiple instances can coexist with independent navigation
9.5 Known Issues and Compatibility
Active GitHub issues relevant to administrators and developers:
| Issue | Summary |
|---|---|
| #3888 | Decktape PDF export error |
| #3883 | React Markdown fragment directives broken with Vite 8 |
| #3821 | npm install error |
| #3895 | v6 Dracula theme background color incorrect |
| #3839 | Whitespace normalization corruption |
| #3894 | v6 not available on CDN |
| #3749 | Incorrect TypeScript typing for keyboard config |
| #3860 | PDF generates very large files |
| #3884 | Fade-out / fade-in-then-out causes blank slides |
| #3878 | Vertical background offset incorrect |
| #3869 | .background CSS not scoped to reveal.js (leaks to page) |
Caution
Issue #3869 reports that
.backgroundCSS selectors in reveal.js themes are not scoped and can leak styles to the host page. When embedding reveal.js in a larger application, use CSS containment or shadow DOM to prevent style conflicts.
9.6 Troubleshooting
Presentation Root Not Found
Error: "Unable to find presentation root (<div class="reveal">)"
Cause: Reveal.initialize() runs before the .reveal div exists in the DOM, or the element is missing entirely.
Solution: Ensure the HTML contains <div class="reveal"><div class="slides">...</div></div> and that initialization runs after DOM ready (e.g., in a DOMContentLoaded handler or at the end of <body>).
Prevention: Always verify DOM structure before calling initialize().
Plugins Not Loading
Error: ready event never fires; presentation appears stuck.
Cause: A plugin's init() method returned a Promise that never resolves or threw an error.
Solution: Check browser console for plugin initialization errors. Test with plugins: [] to confirm the issue is plugin-related. Add each plugin back one at a time to identify the failing one.
Prevention: Always handle errors in async plugin init() methods. Test plugin initialization in isolation.
PDF Export Issues
Error: Blank pages, missing content, or excessively large file size in PDF export.
Cause: Fragment-based pagination (pdfSeparateFragments: true) generates a page per fragment step. Complex backgrounds and media increase file size.
Solution: Set pdfSeparateFragments: false to reduce page count. Use pdfMaxPagesPerSlide to cap pages per slide. Remove or simplify heavy backgrounds before export.
Prevention: Test PDF export early in presentation development. Use ?print-pdf in the URL to preview print layout.
npm Install Failures
Error: Various errors during npm install (see issue #3821).
Cause: Node.js version below the required minimum (20.19.0) or platform-specific dependency issues.
Solution: Verify Node.js version with node --version. Ensure it meets the >= 20.19.0 requirement in package.json engines.
Prevention: Use nvm or fnm to manage Node.js versions and pin to a compatible version.