This is a simple application that allows the incorporation of a Reveal.js presentation into a NextJS / React application.
- NextJS / React web app
- Incoroporates Reveal slides as content
- HTML content is loaded from a remote (or local) URL
- React element inside the HTML content can be created as needed
- Slide show can be multiplexed between controller and clients
- Supports build and export that can be hosted statically (eg. with GitHub Pages)
Multiplexing allows the controller to update all the clients as it moves through the presentation. Effectively, they follow along...
If you've just cloned the repository, you probably need to install the node packages:
npm installNow you can launch the development server:
npm run devIt'll serve the application at: localhost:3000
The following notes outline an approach to developing a Next/React application that incorporates a Reveal presentation.
brew install node
npm install -g npxnpx create-next-app@latestThere are a few react-reveal packages that wrap Reveal for React/Next applications, but they're a little out of date now (the latest was last updated 3 years ago), and aren't really suited to Next or server-side rendering without some extra work.
Install reveal.js directly:
npm install reveal.jsSee components/Presentation.tsx
reveal.js will only run in a browser environment, as it needs access to client specific javascript objects, such as navigator. It also needs to be able to see the div elements with reveal and slides CSS classes, as soon as it is created. This code forces Next to only invoke it in a browser...
This is an invocation for Next - telling it that the component needs client-side rendering:
"use client";Reveal is only created inside a useEffect - which is called when the page and divs are ready.
The embedded option is set to true - and this helps to incorporate other layout and elements alongside the presentation. As the presentation no longer automatically fills the page, the reveal div will need to have its size specified in CSS...
NB. The configuration also includes multiplex information and dependencies. See below for more information.
Reveal is only initialised once the content inside the presentation is ready. See below for details of how the content is retrieved and rendered.
See: app/globals.css
The global CSS has been simplified and adjusted to help fit the presentation to the page. If you are using another framework, such as MUI, you may need to solve this another way.
Here, you can see that margin and padding on html and body have been zeroed. This removes any whitespace around the edges of the page.
body has also been set to display: flex (in column direction), which will allow us to resize the presentation to fit below any layout above it.
See: app/slides/page.tsx
dynamic is used to import the Presentation element dynamically, with ssr: false to prevent server-side rendering.
The Presentation element has a src parameter, and this is passed to an internal PresentationContent element which uses the SWR library to fetch the content. This content is then enriched (React elements are created where needed inside it), and then rendered inside the Presentation.
npm install swrSome of the content is regular HTML, but some of the elements are React components. There are a number of packages that might help us convert the HTML and manage React components:
| Library | Last updated |
|---|---|
| html-react-parser | recently |
| html-to-react | recently |
| 6 years ago |
Install html-react-parser
npm install html-react-parserNB. html-react-parser is simple to use, but not XSS-safe, and should be used with caution. PresentationContent manages replacement of individual React elements by type:
const options = {
replace: (domNode: any) => {
if (domNode instanceof Element && domNode.attribs) {
switch (domNode.tagName) {
case "question":
console.log("Enriching question tag...");
let question = domNode.attribs["question"];
let explanation = domNode.attribs["explanation"];
let instruction = domNode.attribs["instruction"];
if (question && explanation && instruction) {
return (
<Question
question={question}
explanation={explanation}
instruction={instruction}
/>
);
}
break;
}
}
},
};Here, Question also incorporates a Rating element, from: react-rating
npm install react-ratingMultiplexing allows a controller presentation to send its state to client presentations on other devices (ie. to allow them to follow along).
There are 3 components:
- Any number of client presentations
- A controller* presentation, with the same slides
- A socket.io based server that passes messages between the various presentations
*Sometimes referred to as a master presentation.
This demo uses the server at: https://reveal-multiplex.glitch.me/
const SOCKET_IO_SERVER = "https://reveal-multiplex.glitch.me/";- To test locally, you could also run your own server.
- A production system should host its own server.
- See: reveal/multiplex
Install the multiplex plugin:
npm install reveal-multiplexThe Presentation component in presentation.tsx accepts several parameters:
-
secret(a secret to permit control, ornullif acting as the client) -
id(the id of the presentation) -
role(not currently used) -
To collect a fresh secret and id from the server, visit: https://reveal-multiplex.glitch.me/token
The multiplex plugin is configured during initialization of Reveal:
multiplex: {
secret: secret,
id: id,
url: SOCKET_IO_SERVER
},
dependencies: [
{ src: 'https://reveal-multiplex.glitch.me/socket.io/socket.io.js', async: true },
{ src: 'https://reveal-multiplex.glitch.me/master.js', async: true },
{ src: 'https://reveal-multiplex.glitch.me/client.js', async: true },
]Because these dependencies rely on being able to find Reveal as a global variable, we also add this, just before initialization:
window.Reveal = reveal;Not all imports agree on the version of React to use, and this can lead to difficulties with imported elements (such as the Rating element used in Question.tsx). In tsconfig.json add the following to $.compilerOptions.paths to enforce use of the same version:
"react": ["./node_modules/@types/react"]In Presentation.tsx, window.Reveal is explicitly set to ensure that it is available to the multiplexing scripts imported as dependencies of Reveal. TypeScript is strict during a production build, and rejects this as it thinks Reveal is already the name of the module.
To instruct the compiler to overlook TypeScript errors, precede the line with // @ts-ignore, as here:
// @ts-ignore
window.Reveal = reveal;Modify next.config.js to set output to export:
const nextConfig = { output: "export" };Build the project:
npx next buildStatic output is put into the out directory by default.
GitHub Pages can serve the static content either from the root directory of a repository, or from the docs/ directory. You can place the output from out into another repository, and serve it from there by enabling GitHub Pages.
However, directories that are prefixed with _ (underscore) are ignored by default. To work around this, add an empty file called .nojekyll at the root of the repository (see: this blog post about it).
