# reveal.js -- User Guide

reveal.js v6.0.1 is the HTML presentation framework that transforms markup into polished, interactive slide decks with animated transitions, speaker notes, code highlighting, math rendering, and more. Created by Hakim El Hattab. MIT licensed.

This guide covers everything you need to create, configure, and deliver presentations with reveal.js. For architecture details, plugin internals, and deployment strategies, see the **Admin Guide**.

---

## 1. Quick Start

### 1.1 Fastest Path: CDN-Free Local Setup

Create an `index.html` file with this minimal structure:

```html
<!doctype html>
<html>
  <head>
    <link rel="stylesheet" href="dist/reset.css" />
    <link rel="stylesheet" href="dist/reveal.css" />
    <link rel="stylesheet" href="dist/theme/black.css" />
  </head>
  <body>
    <div class="reveal">
      <div class="slides">
        <section>Slide 1</section>
        <section>Slide 2</section>
        <section>Slide 3</section>
      </div>
    </div>
    <script src="dist/reveal.esm.js" type="module"></script>
    <script type="module">
      import Reveal from './dist/reveal.esm.js';
      Reveal.initialize();
    </script>
  </body>
</html>
```

### 1.2 Using npm

```bash
npm install reveal.js
```

Then import in your JavaScript:

```javascript
import Reveal from 'reveal.js';
import 'reveal.js/dist/reset.css';
import 'reveal.js/dist/reveal.css';
import 'reveal.js/dist/theme/black.css';

let deck = new Reveal();
deck.initialize();
```

### 1.3 Full Development Setup

Clone the repository and run the dev server:

```bash
git clone https://github.com/hakimel/reveal.js.git
cd reveal.js
npm install
npm start
```

The Vite dev server launches at `http://localhost:8000` by default. Change the port with the `npm_config_port` environment variable:

```bash
PORT=3000 npm start
```

> [!NOTE]
> reveal.js requires Node.js 20.19.0 or later. The project uses Vite 8 for development and TypeScript 6 for type checking.

---

## 2. Installation

### 2.1 npm (Recommended)

```bash
npm install reveal.js
```

Package exports available after installation:

| Import Path | Content |
|---|---|
| `reveal.js` | Core library (ESM + CJS) |
| `reveal.js/dist/reveal.css` | Core presentation CSS |
| `reveal.js/dist/reset.css` | CSS reset |
| `reveal.js/dist/theme/*.css` | Theme stylesheets |
| `reveal.js/plugin/highlight` | Syntax highlighting plugin |
| `reveal.js/plugin/markdown` | Markdown plugin |
| `reveal.js/plugin/math` | Math rendering plugin |
| `reveal.js/plugin/notes` | Speaker notes plugin |
| `reveal.js/plugin/search` | In-slide search plugin |
| `reveal.js/plugin/zoom` | Element zoom plugin |

### 2.2 From Source

```bash
git clone https://github.com/hakimel/reveal.js.git
cd reveal.js
npm install
npm run build
```

The build outputs to `dist/`:
- `dist/reveal.esm.js` -- ES module build
- `dist/reveal.js` -- UMD build
- `dist/reveal.css` -- core styles
- `dist/theme/` -- compiled theme CSS

### 2.3 React Integration

For React applications, use the official wrapper:

```bash
npm install @revealjs/react reveal.js
```

Requires React 18+ and reveal.js 5+ as peer dependencies.

```jsx
import { Deck, Slide, Fragment } from '@revealjs/react';

function Presentation() {
  return (
    <Deck>
      <Slide>
        <h1>Hello World</h1>
        <Fragment>
          <p>This appears on click</p>
        </Fragment>
      </Slide>
    </Deck>
  );
}
```

Available components: `Deck`, `Slide`, `Stack`, `Markdown`, `Fragment`, `Code`.
Available hook: `useReveal()` (returns the Reveal instance from `RevealContext`).

> [!TIP]
> When using `@revealjs/react` with Vite 8, be aware that Markdown fragment directives may not work correctly (see GitHub issue #3883). Use JSX `Fragment` components as a workaround.

---

## 3. Configuration

### 3.1 Initializing with Options

Pass a configuration object to `Reveal.initialize()`:

```javascript
Reveal.initialize({
  width: 960,
  height: 700,
  controls: true,
  progress: true,
  hash: true,
  transition: 'slide',
  plugins: [ RevealHighlight, RevealNotes ]
});
```

### 3.2 Presentation Size and Scaling

| Option | Default | Description |
|---|---|---|
| `width` | `960` | Base width in pixels |
| `height` | `700` | Base height in pixels |
| `margin` | `0.04` | Empty space around content (as a factor) |
| `minScale` | `0.2` | Smallest scale factor applied |
| `maxScale` | `2.0` | Largest scale factor applied |

reveal.js scales presentations to fit any display while maintaining aspect ratio. The `margin` value creates breathing room around slides -- `0.04` means 4% of the viewport on each side.

### 3.3 Controls and Navigation

| Option | Default | Description |
|---|---|---|
| `controls` | `true` | Show navigation arrows |
| `controlsTutorial` | `true` | Highlight arrows on first encounter |
| `controlsLayout` | `'bottom-right'` | Arrow position |
| `controlsBackArrows` | `'faded'` | Back arrow visibility (`'faded'`, `'hidden'`, `'visible'`) |
| `progress` | `true` | Show progress bar at bottom |
| `slideNumber` | `false` | Display slide number (see below) |
| `showSlideNumber` | `'all'` | Where slide numbers appear (`'all'`, `'print'`, `'speaker'`) |
| `keyboard` | `true` | Enable keyboard navigation |
| `keyboardCondition` | `null` | Condition function for keyboard activation |
| `touch` | `true` | Enable touch navigation |
| `loop` | `false` | Loop the presentation |
| `rtl` | `false` | Right-to-left slide flow |
| `navigationMode` | `'default'` | Navigation model (`'default'`, `'linear'`, `'grid'`) |
| `shuffle` | `false` | Randomize slide order on init |
| `overview` | `true` | Enable overview mode |
| `center` | `true` | Vertically center slide content |
| `jumpToSlide` | `true` | Allow jumping to slides via slide number |
| `help` | `true` | Show `?` help overlay |
| `pause` | `true` | Allow pausing with `B` or `.` |

**Slide number formats:**

```javascript
// Boolean
slideNumber: true       // e.g. "3"

// String format
slideNumber: 'h.v'     // "2.1" (horizontal.vertical)
slideNumber: 'h/v'     // "2/1"
slideNumber: 'c'       // "5" (flattened count)
slideNumber: 'c/t'     // "5/12" (current/total)
```

### 3.4 Transitions

| Option | Default | Description |
|---|---|---|
| `transition` | `'slide'` | Slide transition style |
| `transitionSpeed` | `'default'` | Transition speed |
| `backgroundTransition` | `'fade'` | Background transition style |

**Transition styles:** `none`, `fade`, `slide`, `convex`, `concave`, `zoom`

**Transition speeds:** `default`, `fast`, `slow`

Override per-slide with data attributes:

```html
<section data-transition="zoom">
  <h2>This slide zooms in</h2>
</section>

<section data-transition="fade-in slide-out">
  <h2>Fade in, slide out</h2>
</section>
```

### 3.5 Fragments

| Option | Default | Description |
|---|---|---|
| `fragments` | `true` | Enable fragment animations |
| `fragmentInURL` | `true` | Track fragment state in URL |
| `sortFragmentsOnSync` | `true` | Auto-sort fragments by DOM order |

### 3.6 Media and Embedding

| Option | Default | Description |
|---|---|---|
| `autoPlayMedia` | `null` | Auto-play `<video>` and `<audio>` (`null` = only if `autoplay` attr present) |
| `preloadIframes` | `null` | Pre-load iframes (`null` = only if `data-preload` attr present) |
| `preventIframeAutoFocus` | `false` | Prevent iframes from stealing focus |
| `embedded` | `false` | Run in embedded mode (no keyboard capture) |
| `previewLinks` | `false` | Open links in preview overlay instead of navigating |
| `showNotes` | `false` | Show speaker notes in presentation |
| `showHiddenSlides` | `false` | Show slides marked as hidden |
| `display` | `'block'` | CSS display property for slides |

### 3.7 Auto-Slide

| Option | Default | Description |
|---|---|---|
| `autoSlide` | `0` | Milliseconds between auto-advance (0 = disabled) |
| `autoSlideStoppable` | `true` | Allow user input to stop auto-slide |
| `autoSlideMethod` | `null` | Custom function for auto-slide navigation |
| `defaultTiming` | `null` | Default seconds per slide (used by speaker timer) |

```javascript
// Auto-advance every 5 seconds
Reveal.initialize({ autoSlide: 5000 });
```

Override per-slide:

```html
<section data-autoslide="10000">
  <p>This slide stays for 10 seconds</p>
</section>
```

### 3.8 Cursor Behavior

| Option | Default | Description |
|---|---|---|
| `mouseWheel` | `false` | Navigate slides with scroll wheel |
| `hideInactiveCursor` | `true` | Hide cursor when idle |
| `hideCursorTime` | `5000` | Milliseconds before cursor hides |

### 3.9 Hash and History

| Option | Default | Description |
|---|---|---|
| `hash` | `false` | Reflect current slide in URL hash |
| `respondToHashChanges` | `true` | Navigate when URL hash changes |
| `history` | `false` | Push each slide change to browser history |

> [!IMPORTANT]
> When `hash: true` is enabled, each slide gets a unique URL. This is essential for sharing links to specific slides but may conflict with some single-page application routers.

### 3.10 Parallax Backgrounds

```javascript
Reveal.initialize({
  parallaxBackgroundImage: 'https://example.com/bg.jpg',
  parallaxBackgroundSize: '2100px 900px',
  parallaxBackgroundRepeat: '',
  parallaxBackgroundPosition: '',
  parallaxBackgroundHorizontal: 200,
  parallaxBackgroundVertical: 50
});
```

### 3.11 Views and Scroll Mode

| Option | Default | Description |
|---|---|---|
| `view` | `null` | View mode (`null`, `'print'`, `'scroll'`) |
| `scrollLayout` | `'full'` | Scroll view layout |
| `scrollSnap` | `'mandatory'` | CSS scroll-snap behavior |
| `scrollProgress` | `'auto'` | Progress indicator in scroll view |
| `scrollActivationWidth` | `435` | Viewport width below which scroll view activates |

### 3.12 PDF Export Settings

| Option | Default | Description |
|---|---|---|
| `pdfMaxPagesPerSlide` | `Infinity` | Limit pages generated per slide |
| `pdfSeparateFragments` | `true` | Each fragment step gets its own page |
| `pdfPageHeightOffset` | `-1` | Height adjustment for PDF pages |

### 3.13 View Distance (Performance)

| Option | Default | Description |
|---|---|---|
| `viewDistance` | `3` | Number of slides away from current to pre-render |
| `mobileViewDistance` | `2` | Pre-render distance on mobile |

Reducing these values improves performance for presentations with heavy media content.

### 3.14 PostMessage API

| Option | Default | Description |
|---|---|---|
| `postMessage` | `true` | Enable postMessage API |
| `postMessageEvents` | `false` | Dispatch reveal.js events via postMessage |
| `focusBodyOnPageVisibilityChange` | `true` | Focus body when page becomes visible |

For the full configuration reference with internal options and architecture details, see the **Admin Guide**.

---

## 4. Core Usage

### 4.1 Slide Structure

Every reveal.js presentation requires this DOM structure:

```html
<div class="reveal">
  <div class="slides">
    <section>Horizontal Slide 1</section>
    <section>Horizontal Slide 2</section>
  </div>
</div>
```

#### Vertical Slides

Nest `<section>` elements to create vertical slide stacks:

```html
<section>
  <section>Vertical Slide 1 (top)</section>
  <section>Vertical Slide 2</section>
  <section>Vertical Slide 3</section>
</section>
```

Navigate down through vertical slides, then right to the next horizontal group.

### 4.2 Themes

reveal.js ships with 14 themes:

| Theme | Description |
|---|---|
| `beige` | Warm beige with serif headings |
| `black` | Black background, white text (default) |
| `black-contrast` | High-contrast black variant |
| `blood` | Dark with red accents |
| `dracula` | Popular dark color scheme |
| `league` | Gray textured background |
| `moon` | Dark blue background |
| `night` | Black with thick heading fonts |
| `serif` | Cappuccino tones with serif fonts |
| `simple` | Clean white background |
| `sky` | Light blue gradient |
| `solarized` | Solarized color palette |
| `white` | White background, dark text |
| `white-contrast` | High-contrast white variant |

Switch themes by changing the theme stylesheet:

```html
<!-- Pick one -->
<link rel="stylesheet" href="dist/theme/dracula.css" />
<link rel="stylesheet" href="dist/theme/moon.css" />
<link rel="stylesheet" href="dist/theme/serif.css" />
```

Or when installed via npm:

```javascript
import 'reveal.js/dist/theme/dracula.css';
```

### 4.3 Slide Backgrounds

#### Color Backgrounds

```html
<section data-background-color="#4d7e65">
  <h2>Solid color</h2>
</section>

<section data-background-gradient="linear-gradient(to bottom, #283b95, #17b2c3)">
  <h2>Gradient</h2>
</section>
```

#### Image Backgrounds

```html
<section data-background-image="image.jpg"
         data-background-size="cover"
         data-background-position="center"
         data-background-repeat="no-repeat"
         data-background-opacity="0.5">
  <h2>Image background</h2>
</section>
```

#### Video Backgrounds

```html
<section data-background-video="video.mp4"
         data-background-video-loop
         data-background-video-muted>
  <h2>Video background</h2>
</section>
```

#### Iframe Backgrounds

```html
<section data-background-iframe="https://example.com"
         data-background-interactive>
  <h2>Interactive iframe background</h2>
</section>
```

Use `data-preload` on iframe backgrounds to load them before the slide is reached. The `data-background-interactive` attribute allows clicking and scrolling within the iframe.

All background data attributes:

| Attribute | Purpose |
|---|---|
| `data-background` | Shorthand for color or image |
| `data-background-color` | CSS color value |
| `data-background-gradient` | CSS gradient value |
| `data-background-image` | Image URL |
| `data-background-size` | CSS background-size |
| `data-background-position` | CSS background-position |
| `data-background-repeat` | CSS background-repeat |
| `data-background-opacity` | Opacity (0-1) |
| `data-background-video` | Video URL |
| `data-background-video-loop` | Loop the video |
| `data-background-video-muted` | Mute the video |
| `data-background-iframe` | Iframe URL |
| `data-background-interactive` | Enable iframe interaction |
| `data-background-transition` | Per-slide background transition |
| `data-preload` | Pre-load this background |

### 4.4 Fragments (Step-by-Step Reveals)

Wrap elements in the `fragment` class to reveal them incrementally:

```html
<section>
  <p class="fragment">Appears first</p>
  <p class="fragment">Appears second</p>
  <p class="fragment">Appears third</p>
</section>
```

#### Fragment Animations

| Class | Effect |
|---|---|
| `fade-out` | Fade out on step |
| `fade-up` | Slide up while fading in |
| `fade-down` | Slide down while fading in |
| `fade-left` | Slide left while fading in |
| `fade-right` | Slide right while fading in |
| `fade-in-then-out` | Fade in, then fade out on next step |
| `fade-in-then-semi-out` | Fade in, then dim on next step |
| `grow` | Scale up |
| `shrink` | Scale down |
| `strike` | Strikethrough text |
| `highlight-red` | Turn text red |
| `highlight-blue` | Turn text blue |
| `highlight-green` | Turn text green |
| `highlight-current-red` | Red on current step, revert on next |
| `highlight-current-blue` | Blue on current step, revert on next |
| `highlight-current-green` | Green on current step, revert on next |

```html
<p class="fragment fade-up">Slides up</p>
<p class="fragment highlight-red">Turns red</p>
<p class="fragment fade-in-then-out">Appears then disappears</p>
```

#### Custom Fragment Order

Use `data-fragment-index` to control the order:

```html
<p class="fragment" data-fragment-index="3">Third</p>
<p class="fragment" data-fragment-index="1">First</p>
<p class="fragment" data-fragment-index="2">Second</p>
```

> [!WARNING]
> Fragment animations `fade-out` and `fade-in-then-out` may produce blank slides under certain conditions (GitHub issue #3884). Test these animations with your specific content before presenting.

### 4.5 Speaker Notes

Add notes visible only in the speaker view:

```html
<section>
  <h2>My Slide</h2>
  <aside class="notes">
    These notes are only visible in speaker view.
    You can use <strong>HTML</strong> formatting here.
  </aside>
</section>
```

Or use the `data-notes` attribute for plain text:

```html
<section data-notes="Simple plain text note for this slide">
  <h2>My Slide</h2>
</section>
```

Open speaker view by pressing **S** or appending `?notes` to the URL.

### 4.6 Keyboard Shortcuts

| Key | Action |
|---|---|
| Arrow keys | Navigate between slides |
| Space | Next slide/fragment |
| Shift + Space | Previous slide/fragment |
| S | Open speaker notes window |
| O or Esc | Toggle overview mode |
| F | Toggle fullscreen |
| B or . | Pause/blackout |
| ? | Toggle help overlay |
| Ctrl + Shift + F | Open search (requires Search plugin) |
| Alt + click | Zoom into element (requires Zoom plugin; Ctrl + click on Linux) |

### 4.7 Slide Visibility

Control whether slides appear in the deck:

```html
<!-- Completely removed from the flow -->
<section data-visibility="hidden">
  <h2>This slide won't appear</h2>
</section>

<!-- Visible but not counted in slide numbers -->
<section data-visibility="uncounted">
  <h2>Bonus slide (unnumbered)</h2>
</section>
```

### 4.8 Auto-Animate

Create smooth transitions between consecutive slides by adding `data-auto-animate`:

```html
<section data-auto-animate>
  <h1>Step 1</h1>
</section>
<section data-auto-animate>
  <h1 style="color: red;">Step 1</h1>
  <p>New content appears</p>
</section>
```

reveal.js automatically animates matching elements between slides. It matches elements by:
1. Explicit `data-id` attribute (strongest match)
2. Tag name + text content (for text, images, videos, code blocks)

#### Matching with data-id

```html
<section data-auto-animate>
  <div data-id="box" style="width: 100px; height: 100px; background: blue;"></div>
</section>
<section data-auto-animate>
  <div data-id="box" style="width: 300px; height: 100px; background: red;"></div>
</section>
```

#### Controlling Animation Behavior

Global configuration:

| Option | Default | Description |
|---|---|---|
| `autoAnimate` | `true` | Enable auto-animate globally |
| `autoAnimateEasing` | `'ease'` | CSS easing function |
| `autoAnimateDuration` | `1.0` | Animation duration in seconds |
| `autoAnimateUnmatched` | `true` | Animate unmatched elements (fade in) |
| `autoAnimateMatcher` | `null` | Custom matching function |

Animatable CSS properties (default):

`opacity`, `color`, `background-color`, `padding`, `font-size`, `line-height`, `letter-spacing`, `border-width`, `border-color`, `border-radius`, `outline`, `outline-offset`

Per-element overrides:

```html
<div data-id="box"
     data-auto-animate-easing="ease-in-out"
     data-auto-animate-duration="0.8"
     data-auto-animate-delay="0.2">
</div>
```

#### Grouping and Breaking Chains

Use `data-auto-animate-id` to group non-adjacent auto-animate slides:

```html
<section data-auto-animate data-auto-animate-id="group-a">Step A1</section>
<section data-auto-animate data-auto-animate-id="group-b">Step B1</section>
<section data-auto-animate data-auto-animate-id="group-a">Step A2</section>
```

Use `data-auto-animate-restart` to break an animation chain:

```html
<section data-auto-animate>Step 1</section>
<section data-auto-animate>Step 2 (animates from Step 1)</section>
<section data-auto-animate data-auto-animate-restart>Step 3 (fresh start)</section>
```

### 4.9 Links and Slide References

Link to specific slides within your presentation:

```html
<a href="#/2">Go to slide 3 (zero-indexed)</a>
<a href="#/2/1">Go to slide 3, vertical slide 2</a>
```

With `hash: true`, you can also use slide IDs:

```html
<section id="conclusion">
  <h2>Conclusion</h2>
</section>

<!-- Link to it -->
<a href="#/conclusion">Jump to conclusion</a>
```

### 4.10 Media: Images, Video, Audio

#### Lazy-Loading Media

Use `data-src` instead of `src` to lazy-load:

```html
<section>
  <img data-src="large-image.jpg" />
  <video data-src="video.mp4"></video>
  <iframe data-src="https://example.com"></iframe>
</section>
```

#### Embedded Video

```html
<section>
  <video data-autoplay src="video.mp4"></video>
</section>
```

With `autoPlayMedia: null` (the default), videos only auto-play if they have the `data-autoplay` attribute.

---

## 5. Common Tasks

### 5.1 Writing Slides in Markdown

Load the Markdown plugin:

```javascript
import Reveal from 'reveal.js';
import RevealMarkdown from 'reveal.js/plugin/markdown';

Reveal.initialize({
  plugins: [ RevealMarkdown ]
});
```

#### Inline Markdown

```html
<section data-markdown>
  <textarea data-template>
    ## Slide Title

    A paragraph with **bold** and *italic*.

    - Item one
    - Item two
    ---
    ## Next Slide

    More content here.
  </textarea>
</section>
```

The default horizontal separator is `---`. Configure custom separators:

```html
<section data-markdown
         data-separator="^\n---\n$"
         data-separator-vertical="^\n--\n$"
         data-separator-notes="^notes:">
  <textarea data-template>
    ## Slide 1
    Content
    --
    ## Vertical Slide 1.1
    More content

    notes:
    Speaker notes for this slide
    ---
    ## Slide 2
  </textarea>
</section>
```

#### External Markdown Files

```html
<section data-markdown="slides.md"
         data-separator="^\n---\n$"
         data-separator-vertical="^\n--\n$"
         data-separator-notes="^notes:"
         data-charset="utf-8">
</section>
```

#### Markdown Plugin Configuration

```javascript
Reveal.initialize({
  markdown: {
    separator: '^\r?\n---\r?\n$',
    verticalSeparator: '^\r?\n--\r?\n$',
    notesSeparator: 'notes:',
    animateLists: false,
    smartypants: false,
    gfm: true,
    breaks: false
  },
  plugins: [ RevealMarkdown ]
});
```

#### Element Attributes in Markdown

Apply HTML attributes to Markdown elements:

```markdown
## Title <!-- .element: class="fragment" data-fragment-index="1" -->

- Item 1 <!-- .element: class="fragment fade-up" -->
- Item 2 <!-- .element: class="fragment fade-up" -->
```

Apply attributes to the slide itself:

```markdown
<!-- .slide: data-background="#ff0000" -->
## Red Background Slide
```

### 5.2 Syntax-Highlighted Code

Load the Highlight plugin:

```javascript
import Reveal from 'reveal.js';
import RevealHighlight from 'reveal.js/plugin/highlight';

Reveal.initialize({
  plugins: [ RevealHighlight ]
});
```

Include a highlight.js theme:

```html
<link rel="stylesheet" href="plugin/highlight/monokai.css" />
```

#### Basic Code Block

```html
<section>
  <pre><code data-trim data-noescape class="language-python">
def greet(name):
    return f"Hello, {name}!"

print(greet("World"))
  </code></pre>
</section>
```

- `data-trim` -- removes leading/trailing whitespace
- `data-noescape` -- prevents HTML entity escaping

#### Line Numbers

```html
<pre><code data-line-numbers class="language-javascript">
const x = 1;
const y = 2;
const z = x + y;
console.log(z);
</code></pre>
```

Highlight specific lines:

```html
<!-- Highlight lines 1 and 3 -->
<pre><code data-line-numbers="1,3" class="language-javascript">
const x = 1;
const y = 2;
const z = x + y;
</code></pre>
```

#### Step-Through Highlighting

Animate through different highlighted lines as fragments:

```html
<pre><code data-line-numbers="1|3-4|6" class="language-javascript">
const width = 960;
const height = 700;
const area = width * height;
const perimeter = 2 * (width + height);

console.log(`Area: ${area}, Perimeter: ${perimeter}`);
</code></pre>
```

Each pipe-separated group becomes a fragment step: first line 1 highlights, then lines 3-4, then line 6.

#### Highlight Plugin Configuration

| Option | Default | Description |
|---|---|---|
| `highlightOnLoad` | `true` | Auto-highlight all code blocks |
| `escapeHTML` | `true` | Escape HTML in code blocks |
| `beforeHighlight` | `null` | Callback before highlighting |

```javascript
Reveal.initialize({
  highlight: {
    highlightOnLoad: true,
    escapeHTML: true
  },
  plugins: [ RevealHighlight ]
});
```

### 5.3 Rendering Math Equations

Load one of the Math sub-plugins:

```javascript
import Reveal from 'reveal.js';
import RevealMath from 'reveal.js/plugin/math';

Reveal.initialize({
  plugins: [ RevealMath.KaTeX ]
  // or: RevealMath.MathJax2, RevealMath.MathJax3, RevealMath.MathJax4
});
```

#### KaTeX (Fastest)

```javascript
Reveal.initialize({
  katex: {
    version: 'latest',
    delimiters: [
      { left: '$$', right: '$$', display: true },
      { left: '$', right: '$', display: false },
      { left: '\\(', right: '\\)', display: false },
      { left: '\\[', right: '\\]', display: true }
    ],
    ignoredTags: ['script', 'noscript', 'style', 'textarea', 'pre']
  },
  plugins: [ RevealMath.KaTeX ]
});
```

#### MathJax 2

```javascript
Reveal.initialize({
  mathjax2: {
    mathjax: 'https://cdn.jsdelivr.net/npm/mathjax@2/MathJax.js',
    config: 'TeX-AMS_HTML-full',
    tex2jax: {
      inlineMath: [['$', '$'], ['\\(', '\\)']],
      skipTags: ['script', 'noscript', 'style', 'textarea', 'pre']
    }
  },
  plugins: [ RevealMath.MathJax2 ]
});
```

#### Using Math in Slides

```html
<section>
  <h2>Euler's Identity</h2>
  <p>$$e^{i\pi} + 1 = 0$$</p>
  <p>Inline math: $a^2 + b^2 = c^2$</p>
</section>
```

### 5.4 Using Speaker Notes

Load the Notes plugin:

```javascript
import Reveal from 'reveal.js';
import RevealNotes from 'reveal.js/plugin/notes';

Reveal.initialize({
  plugins: [ RevealNotes ]
});
```

The speaker view opens in a separate window showing:
- Current slide
- Next slide preview
- Speaker notes
- Elapsed time and timer

Open with **S** key or `?notes` query parameter.

### 5.5 Searching Within Slides

Load the Search plugin:

```javascript
import Reveal from 'reveal.js';
import RevealSearch from 'reveal.js/plugin/search';

Reveal.initialize({
  plugins: [ RevealSearch ]
});
```

Open search with **Ctrl + Shift + F**. The plugin provides programmatic access via `open()`, `close()`, and `toggle()` methods.

### 5.6 Zooming Into Elements

Load the Zoom plugin:

```javascript
import Reveal from 'reveal.js';
import RevealZoom from 'reveal.js/plugin/zoom';

Reveal.initialize({
  plugins: [ RevealZoom ]
});
```

**Alt + click** (or **Ctrl + click** on Linux) on any element to zoom in. Click again to zoom out.

Configuration:

| Option | Default | Description |
|---|---|---|
| `zoomKey` | `'alt'` | Modifier key for zoom (`'alt'` or `'ctrl'`) |
| `zoomLevel` | `2` | Zoom magnification factor |

### 5.7 Exporting to PDF

Add `?print-pdf` to the presentation URL:

```
http://localhost:8000/?print-pdf
```

Then use the browser's print dialog (Ctrl + P / Cmd + P):
1. Set destination to "Save as PDF"
2. Set layout to "Landscape"
3. Set margins to "None"
4. Enable "Background graphics"

> [!TIP]
> Set `pdfSeparateFragments: false` to collapse all fragment steps into single slides for more compact PDFs. If your PDFs are very large, reduce the number of high-resolution background images.

#### PDF Configuration

```javascript
Reveal.initialize({
  pdfMaxPagesPerSlide: 1,        // Limit pages per slide
  pdfSeparateFragments: true,     // Each fragment = separate page
  pdfPageHeightOffset: -1         // Fine-tune page height
});
```

### 5.8 Scroll View

Enable scroll view for a document-like reading experience:

```javascript
Reveal.initialize({
  view: 'scroll',
  scrollLayout: 'full',
  scrollSnap: 'mandatory',
  scrollProgress: 'auto'
});
```

On viewports narrower than `scrollActivationWidth` (default: 435px), scroll view activates automatically.

### 5.9 Embedding a Presentation

Embed reveal.js in a page alongside other content:

```html
<div class="reveal" style="width: 800px; height: 600px;">
  <div class="slides">
    <section>Embedded slide</section>
  </div>
</div>

<script type="module">
  import Reveal from 'reveal.js';
  let deck = new Reveal(document.querySelector('.reveal'), {
    embedded: true,
    keyboardCondition: 'focused'
  });
  deck.initialize();
</script>
```

With `embedded: true`, the presentation only captures keyboard events when focused. Multiple independent presentations can coexist on one page.

### 5.10 Using the PostMessage API

Control a presentation embedded in an iframe:

```javascript
// From the parent page
const iframe = document.querySelector('iframe');

// Navigate to slide 2
iframe.contentWindow.postMessage(
  JSON.stringify({ method: 'slide', args: [2] }),
  '*'
);

// Listen for events
window.addEventListener('message', event => {
  const data = JSON.parse(event.data);
  if (data.namespace === 'reveal') {
    console.log('Slide changed:', data.eventName);
  }
});
```

Enable event broadcasting:

```javascript
Reveal.initialize({
  postMessage: true,
  postMessageEvents: true
});
```

### 5.11 Multiple Presentations on One Page

Create independent deck instances by passing a DOM element:

```javascript
let deck1 = new Reveal(document.querySelector('#deck1'), {
  embedded: true
});
deck1.initialize();

let deck2 = new Reveal(document.querySelector('#deck2'), {
  embedded: true
});
deck2.initialize();
```

### 5.12 Loading All Plugins Together

A typical full-featured setup:

```javascript
import Reveal from 'reveal.js';
import RevealHighlight from 'reveal.js/plugin/highlight';
import RevealMarkdown from 'reveal.js/plugin/markdown';
import RevealMath from 'reveal.js/plugin/math';
import RevealNotes from 'reveal.js/plugin/notes';
import RevealSearch from 'reveal.js/plugin/search';
import RevealZoom from 'reveal.js/plugin/zoom';

Reveal.initialize({
  hash: true,
  plugins: [
    RevealHighlight,
    RevealMarkdown,
    RevealMath.KaTeX,
    RevealNotes,
    RevealSearch,
    RevealZoom
  ]
});
```

### 5.13 React Component Recipes

#### Basic React Deck

```jsx
import { Deck, Slide, Fragment, Code } from '@revealjs/react';

function MyPresentation() {
  return (
    <Deck transition="slide" hash={true}>
      <Slide>
        <h1>Welcome</h1>
        <Fragment>
          <p>This fades in</p>
        </Fragment>
      </Slide>
      <Slide backgroundColor="#4d7e65">
        <h2>Code Example</h2>
        <Code language="javascript" lineNumbers="1|3-4">
          {`const x = 1;
const y = 2;
const sum = x + y;
console.log(sum);`}
        </Code>
      </Slide>
    </Deck>
  );
}
```

#### Accessing the Reveal Instance

```jsx
import { useReveal } from '@revealjs/react';

function SlideControls() {
  const reveal = useReveal();

  return (
    <button onClick={() => reveal.next()}>
      Next Slide
    </button>
  );
}
```

#### Vertical Slide Stacks

```jsx
import { Deck, Slide, Stack } from '@revealjs/react';

function Presentation() {
  return (
    <Deck>
      <Slide>Horizontal 1</Slide>
      <Stack>
        <Slide>Vertical 1.1</Slide>
        <Slide>Vertical 1.2</Slide>
      </Stack>
      <Slide>Horizontal 2</Slide>
    </Deck>
  );
}
```

### 5.14 Custom Keyboard Bindings

Add custom key handlers:

```javascript
Reveal.initialize({
  keyboard: {
    // Pause on 'P' key (key code 80)
    80: () => { Reveal.togglePause(); },
    // Go to first slide on 'Home'
    36: () => { Reveal.slide(0); }
  }
});
```

### 5.15 Event Listeners

React to presentation events:

```javascript
Reveal.on('slidechanged', event => {
  console.log(`Moved to slide ${event.indexh}, ${event.indexv}`);
});

Reveal.on('fragmentshown', event => {
  console.log('Fragment shown:', event.fragment);
});

Reveal.on('overviewshown', () => {
  console.log('Overview mode activated');
});

Reveal.on('paused', () => {
  console.log('Presentation paused');
});
```

---

## 6. Troubleshooting

### Error: `Unable to find presentation root`

**Cause:** reveal.js cannot locate the `.reveal` container element in the DOM.

**Solution:** Ensure your HTML contains the required structure. The `div.reveal > div.slides` hierarchy must exist before `Reveal.initialize()` is called:

```html
<div class="reveal">
  <div class="slides">
    <section>At least one slide</section>
  </div>
</div>
```

If initializing dynamically, make sure the DOM is ready (use `DOMContentLoaded` or place the script after the markup).

**Prevention:** Always verify the container structure exists before initializing. When using frameworks like React, use the `@revealjs/react` wrapper which handles this automatically.

### Error: `Please specify a valid media type to preview`

**Cause:** The `previewLinks` feature was activated but could not determine the content type of the linked resource.

**Solution:** Ensure the linked URL returns proper content-type headers, or disable link previews for problematic links by adding `data-preview-link="false"` to the anchor element.

**Prevention:** Only enable `previewLinks: true` when your linked resources are reliably accessible and serve correct headers.

### Problem: npm install fails

**Cause:** Node.js version mismatch or network issues. reveal.js v6.0.1 requires Node.js 20.19.0+ (GitHub issue #3821).

**Solution:**
```bash
node --version
# Must be >= 20.19.0

nvm install 20
nvm use 20
npm install
```

**Prevention:** Use a `.nvmrc` file or check the `engines` field in `package.json` before installing.

### Problem: PDF export generates very large files

**Cause:** High-resolution background images and per-fragment page generation inflate file size (GitHub issue #3860).

**Solution:**
1. Optimize background images before using them
2. Reduce fragment separation:
   ```javascript
   Reveal.initialize({ pdfSeparateFragments: false });
   ```
3. Limit pages per slide:
   ```javascript
   Reveal.initialize({ pdfMaxPagesPerSlide: 1 });
   ```

**Prevention:** Use compressed images and consider whether every fragment step needs its own PDF page.

### Problem: PDF export fails with Decktape

**Cause:** Compatibility issues between Decktape and reveal.js v6 (GitHub issue #3888).

**Solution:** Use the built-in `?print-pdf` approach with browser print instead of Decktape. If Decktape is required, check the issue thread for updated compatibility patches.

**Prevention:** Test PDF export workflow before committing to Decktape for production use.

### Problem: Dracula theme background color changed in v6

**Cause:** The Dracula theme SCSS was updated in v6.0.1 (GitHub issue #3895).

**Solution:** Override the background color in your custom CSS:

```css
.reveal {
  background-color: #282a36; /* Original Dracula background */
}
```

**Prevention:** Pin theme variables explicitly if your design depends on specific colors.

### Problem: Content with multiple spaces is corrupted

**Cause:** Whitespace normalization strips intentional multiple spaces in text content (GitHub issue #3839).

**Solution:** Use `&nbsp;` entities for intentional multiple spaces, or wrap content in `<pre>` tags to preserve whitespace.

**Prevention:** Avoid relying on multiple consecutive spaces in HTML content; use CSS for spacing instead.

### Problem: v6 not available on CDN

**Cause:** The v6 release has not been published to CDN providers yet (GitHub issue #3894).

**Solution:** Install via npm or serve the `dist/` files from your own server after building from source.

**Prevention:** Use npm for production deployments rather than relying on CDN availability for new major versions.

> [!CAUTION]
> If you encounter TypeScript errors related to keyboard configuration types, this is a known typing issue (GitHub issue #3749). Cast the config values as needed or use `// @ts-ignore` as a temporary workaround.

### Problem: Background styling leaks outside reveal.js

**Cause:** The `.background` CSS class is not scoped exclusively to reveal.js (GitHub issue #3869).

**Solution:** Ensure your page's CSS does not use a generic `.background` class, or scope reveal.js within a shadow DOM or iframe.

**Prevention:** Use namespaced CSS classes in your surrounding page to avoid collisions.

### Problem: Vertical background offset calculated incorrectly

**Cause:** Background positioning miscalculation for vertical slides (GitHub issue #3878).

**Solution:** Set explicit background positioning via `data-background-position` on affected vertical slides.

**Prevention:** Test vertical slide backgrounds across different viewport sizes.

---

## 7. FAQ

**Q: What browsers does reveal.js support?**
reveal.js works in all modern evergreen browsers (Chrome, Firefox, Safari, Edge). It uses standard Web APIs and ES modules.

**Q: Can I use reveal.js without npm or Node.js?**
Yes. Download the built files from the GitHub releases page and include `dist/reveal.esm.js` and `dist/reveal.css` directly in your HTML. Node.js is only needed for development (running the dev server, building from source, running tests).

**Q: How do I use multiple plugins together?**
Pass them all in the `plugins` array during initialization. Each plugin registers itself by its ID (e.g., `'highlight'`, `'markdown'`, `'notes'`). See recipe 5.12 for a complete example.

**Q: Can I run multiple presentations on one page?**
Yes. Create separate `new Reveal(element)` instances with `embedded: true`. Each deck operates independently. See recipe 5.11.

**Q: How do I navigate to a specific slide via URL?**
Enable `hash: true` in your config. Slides are addressable as `#/0` (first slide), `#/1/2` (second horizontal, third vertical). Named slides with `id` attributes work as `#/slide-id`.

**Q: How do I customize a theme?**
Create a custom SCSS file that imports the theme template and overrides variables. The theme source files are in `css/theme/source/`. Build with `npm run build` to compile. Alternatively, override styles in a regular CSS file loaded after the theme.

**Q: What is the difference between `view: 'scroll'` and normal mode?**
Scroll view transforms the presentation into a vertically scrollable document, similar to a web page. Normal mode shows one slide at a time with transitions. Scroll view activates automatically on narrow viewports (below `scrollActivationWidth: 435` pixels).

**Q: Can I use reveal.js with React?**
Yes. Install `@revealjs/react` (v0.2.1) which provides `Deck`, `Slide`, `Stack`, `Markdown`, `Fragment`, and `Code` components, plus a `useReveal()` hook. Requires React 18+ and reveal.js 5+.

**Q: How do I add my own JavaScript logic to slides?**
Listen to reveal.js events (see recipe 5.15) or write a custom plugin. The `Reveal.on()` method lets you react to `slidechanged`, `fragmentshown`, `fragmenthidden`, `overviewshown`, `paused`, `resumed`, and other lifecycle events.

**Q: How do I disable specific keyboard shortcuts?**
Pass a keyboard config object that maps key codes to `null`:

```javascript
Reveal.initialize({
  keyboard: {
    27: null  // Disable Escape key (overview toggle)
  }
});
```

> [!NOTE]
> For configuration internals, plugin architecture, build system details, and deployment strategies, see the **Admin Guide** at [hakimel/reveal.js](/hakimel/reveal.js/admin-guide/).

---

_Generated May 2026 from hakimel/reveal.js at 76dc900. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
