Files
emacs-conf/scripts/reveal.js/react/README.md

199 lines
5.9 KiB
Markdown

<p align="center">
<a href="https://revealjs.com">
<img src="https://hakim-static.s3.amazonaws.com/reveal-js/logo/v1/reveal-black-text-sticker.png" alt="reveal.js" width="500">
</a>
</p>
# @revealjs/react
`@revealjs/react` is a thin React wrapper around the [Reveal.js](https://revealjs.com) presentation framework. Describe your slides as React components and let the wrapper handle the rest.
## Installation
Install the package along with its peer dependencies:
```bash
npm i @revealjs/react reveal.js react react-dom
# or
yarn add @revealjs/react reveal.js react react-dom
```
The package ships only the React bindings. You still need to import Reveal CSS, themes, and any plugins your deck uses.
## Set up a deck
Render a `Deck` with one or more `Slide` children and import the core Reveal styles:
```tsx
import { Deck, Slide } from '@revealjs/react';
import 'reveal.js/reveal.css';
import 'reveal.js/theme/black.css';
export function Presentation() {
return (
<Deck>
<Slide>
<h1>Hello</h1>
<p>My first Reveal deck in React.</p>
</Slide>
<Slide background="#111827">
<h2>Second slide</h2>
</Slide>
</Deck>
);
}
```
## Components
Alongside `Deck` and `Slide`, the package ships a few components for common slide patterns. `Fragment` reveals content one step at a time, `Code` renders a syntax-highlighted block via the highlight plugin, and `Stack` groups slides into a vertical column:
```tsx
import { Deck, Slide, Stack, Fragment, Code } from '@revealjs/react';
import RevealHighlight from 'reveal.js/plugin/highlight';
import 'reveal.js/plugin/highlight/monokai.css';
export function Presentation() {
return (
<Deck plugins={[RevealHighlight]}>
<Slide>
<h2>Step by step</h2>
<Fragment animation="fade-up" as="p">First point</Fragment>
<Fragment animation="fade-up" asChild>
<div>Second point</div>
</Fragment>
<Code language="javascript" lineNumbers>
{`console.log('Hello, world!');`}
</Code>
</Slide>
<Stack>
<Slide>Vertical 1</Slide>
<Slide>Vertical 2</Slide>
</Stack>
</Deck>
);
}
```
## Configure Reveal
Pass any Reveal configuration through the `config` prop on `Deck`. Plugins are registered separately via `plugins` and are applied once at initialization time, matching Reveal's plugin lifecycle.
```tsx
import { Deck, Slide } from '@revealjs/react';
import 'reveal.js/reveal.css';
import 'reveal.js/theme/black.css';
import 'reveal.js/plugin/highlight/monokai.css';
import RevealHighlight from 'reveal.js/plugin/highlight';
export function Presentation() {
return (
<Deck
config={{
width: 1280,
height: 720,
hash: true,
controls: true,
progress: true,
transition: 'slide',
}}
plugins={[RevealHighlight]}
>
<Slide>Configured deck</Slide>
</Deck>
);
}
```
`config` maps directly to [Reveal's configuration object](https://revealjs.com/config/). `Slide` supports convenient Reveal slide props such as `background`, `backgroundImage`, `backgroundColor`, `visibility`, `autoAnimate`, `transition`, `transitionSpeed`, `autoSlide`, `notes`, `backgroundInteractive`, and `preload`, while still passing through raw `data-*` attributes to the rendered `<section>` element.
## Subscribe to events
Use event props on `Deck` to respond to Reveal lifecycle and navigation events:
```tsx
import { Deck, Slide } from '@revealjs/react';
export function Presentation() {
return (
<Deck
onReady={(deck) => {
console.log('Reveal ready', deck);
}}
onSync={() => {
console.log('Deck synced');
}}
onSlideChange={(event) => {
console.log('Slide changed', event.indexh, event.indexv);
}}
onFragmentShown={(event) => {
console.log('Fragment shown', event.fragment);
}}
>
<Slide>Intro</Slide>
<Slide>Next</Slide>
</Deck>
);
}
```
## Access the Reveal API
Use `useReveal()` inside the deck tree to call the Reveal API from your own components:
```tsx
import { Deck, Slide, useReveal } from '@revealjs/react';
function NextButton() {
const deck = useReveal();
return <button onClick={() => deck?.next()}>Next slide</button>;
}
export function Presentation() {
return (
<Deck>
<Slide>
<h2>Controlled from React</h2>
<NextButton />
</Slide>
</Deck>
);
}
```
To access the Reveal instance outside of the component tree, pass a `deckRef` to `Deck`:
```tsx
import { useRef } from 'react';
import { Deck, Slide } from '@revealjs/react';
import type { RevealApi } from 'reveal.js';
export function Presentation() {
const deckRef = useRef<RevealApi | null>(null);
return (
<Deck deckRef={deckRef}>
<Slide>Hello</Slide>
</Deck>
);
}
```
## How it works
- `Deck` creates one Reveal instance on mount and destroys it on unmount. Initialization is asynchronous — `onReady` fires once `reveal.initialize()` resolves, after which the instance is also accessible via `useReveal()` and `deckRef`.
- `Deck` calls `reveal.sync()` when the rendered slide structure changes, such as slides being added, removed, reordered, or regrouped into stacks.
- `Slide` handles slide-level `data-*` attribute updates locally with `reveal.syncSlide()`, so ordinary React content updates inside a slide do not trigger a full deck sync.
- `config` is shallow-compared on each render so that `reveal.configure()` is only called when a value actually changes.
- `plugins` are initialization-only, matching Reveal's plugin lifecycle. The prop is captured once on first mount and ignored on subsequent renders.
- Event props are wired with `deck.on()` after initialization and cleaned up with `deck.off()`. Changing a callback between renders swaps the listener automatically.
---
<div align="center">
MIT licensed | Copyright © 2011-2026 Hakim El Hattab, https://hakim.se
</div>