Closed princefishthrower closed 4 years ago
Actually, it works! (At an initial try. Currently, I'm having trouble with getting the slide transition animations to work, but hopefully, it is just a CSS conflict). Here's a most basic react component with hooks example (note that I moved the styles in locally as a quick fix since frameworks like create-react-app do not allow imports from outside the src/ directory!)
import * as React from "react";
import Reveal from 'reveal.js';
import '../../styles/reveal.js/reset.css';
import '../../styles/reveal.js/reveal.css';
import '../../styles/reveal.js/theme/white.css';
export function Presentation() {
React.useEffect(() => {
let deck = new Reveal({
backgroundTransition: 'slide',
transition: 'slide'
})
deck.initialize();
console.log('initialized fool')
},[])
return (
<div className="reveal">
{/* Any section element inside of this container is displayed as a slide */}
<div className="slides" data-transition="slide">
<section data-transition="slide">
<a href="https://revealjs.com">
<img
src="https://static.slid.es/reveal/logo-v1/reveal-white-text.svg"
alt="reveal.js logo"
style={{
height: "180px",
margin: "0 auto 4rem auto",
background: "transparent",
}}
className="demo-logo"
/>
</a>
<h3>The HTML Presentation Framework</h3>
</section>
<section data-transition="slide">
<h2>Hello There</h2>
<p>
reveal.js enables you to create beautiful interactive
slide decks using HTML. This presentation will show you
examples of what it can do.
</p>
</section>
<section data-transition="slide">
<h2>Marvelous List</h2>
<ul>
<li>No order here</li>
<li>Or here</li>
<li>Or here</li>
<li>Or here</li>
</ul>
</section>
<section data-transition="slide">
<h2>Fantastic Ordered List</h2>
<ol>
<li>One is smaller than...</li>
<li>Two is smaller than...</li>
<li>Three!</li>
</ol>
</section>
</div>
</div>
);
}
Also helpful for anyone who comes here. We were also using Bootstrap for our styling, this solution https://github.com/hakimel/reveal.js/issues/1515#issuecomment-432471954 solved the transition issue I mentioned above!
All is well in reveal.js + react.js land!
i can't get this to work in a react project. it inits fine but the content just sits on the page as if Reveal can't access the dom
@simonbarker I had the same problem but got this working. I had to set the width and height of .reveal
.
I have an error:
export 'default' (imported as 'Reveal') was not found in 'reveal.js'
anyone know why?
Yes even I'm having similar issues @DQinYuan
Is there really no other documentation on how to do this? Pasted the code example shown above, not working...
I got this working by having
<div id="root" class="reveal">
in my index.html
, and then
function App() {
useEffect(() => {
let deck = new Reveal({
backgroundTransition: "slide",
transition: "slide",
});
deck.initialize();
});
return (
<div class="slides">
<section>Slide 1</section>
<section>Slide 2</section>
</div>
);
}
in App.js
.
Having the class="reveal"
in App()
didn't seem to work for whatever reason.
I haven't tested it but I think that adding an empty array as useEffect's dependencies might ensure the deck is initialized only once (ie. even if App
component gets rerendered).
Also having the deck
in a ref may be useful to later call methods on it (like deck.current.next()
, deck.current.getSlide(...)
, etc. see https://revealjs.com/api/) :
function App() {
const deck = useRef(null); // keep deck instance in a ref
useEffect(() => {
deck.current = new Reveal({
backgroundTransition: "slide",
transition: "slide",
});
deck.current.initialize();
}, []); // only launch useEffect at first render
return (
<div class="slides">
<section>Slide 1</section>
<section>Slide 2</section>
</div>
);
}
Edit: corrected the ref manipulation
For anyone who is still interested in being able to put react components inside Reveal slides, here is some solutions
Say you have a very simple presentation:
<html>
<head>
<link rel="stylesheet" href="dist/reveal.css">
<link rel="stylesheet" href="dist/theme/white.css">
</head>
<body>
<div class="reveal">
<div class="slides">
<section>Slide 1</section>
<section>Slide 2</section>
</div>
</div>
<script src="dist/reveal.js"></script>
<script>
Reveal.initialize();
</script>
</body>
</html>
The content of the presentation is in the <div class="slides">
element.
If you want to create the contents entirely in React (which you can), then make this div (or preferably the parent div with class="reveal"
https://github.com/hakimel/reveal.js/issues/2784#issuecomment-1818063790) the #root
of your React App (using ReactDOM.createRoot
). Dont forget to add the parts of your presentation's html to your React projects index.html
that are needed to get the slides to work. For the example above, you would need to add:
<link rel="stylesheet" href="dist/reveal.css">
<link rel="stylesheet" href="dist/theme/white.css">
to index.html
's head element and add the scripts
<script src="dist/reveal.js"></script>
<script>
Reveal.initialize();
</script>
somewhere at the end of index.html
's body element.
However, if you want to only sprinkle in components in specific slides, then I would make the react root element a separate element that sits outside of the reveal container div and then use portals to place react component into specific sections.
For a simple Vite project you might end up with an index.html
that looks like:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="stylesheet" href="dist/reveal.css">
<link rel="stylesheet" href="dist/theme/white.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div class="reveal">
<div class="slides">
<section>Slide 1</section>
<section>Slide 2</section>
</div>
</div>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<script src="dist/reveal.js"></script>
<script>
Reveal.initialize();
</script>
</body>
</html>
There is an issue with the examples provided so far and that is the paths for the reveal links and scripts. You need to make sure they are correct and they depend on how you added Reveal to your project. I would recommend using the npm
installation method and then making sure reveal is included in the build (so it ends up in the dist
directory).
Instead of editing/manipulating the index.html
by hand, you can add and initialize reveal in a react app source file like main.tsx
or app.tsx
. You can do it globally outside of app/component functions or inside one of them. In the latter case, you have to be careful not to stack initializations. Only initialize a slide deck once. If you need to reconfigure, use the configure
function or destroy
the deck before initializing again.
To begin, install reveal using npm
:
npm install reveal.js
and to get types for typescript
npm i --save-dev @types/reveal.js
You will need the following imports:
import Reveal from "reveal.js";
import "reveal.js/dist/reveal.css";
import "reveal.js/dist/theme/black.css"; // "black" theme
Finally, just execute the code provided in the initialization section of the Reveal.js docs. Make sure to use what is relevant to your case.
@t-fritsch provides a good example just above https://github.com/hakimel/reveal.js/issues/2784#issuecomment-1818433506
To contribute something to this issue thread that hasnt already been said, I will tackle the re-initialization issue in more detail.
I highly recommend using a ref to the div
that has the reveal
class. But even when you do, if react is in StrictMode
, you will still get the re-initialization problem.
Its very weird issue where the reveal deck is initialized but no flag (like isReady
) indicated it has except the changes made by Reveal to the DOM. The latter is what I check to make sure it hasnt been previously initialized. The alternative is to remove StrictMode but I like that less.
So starting from @t-fritsch's example, I would make the following changes:
function App() {
const deckDivRef = useRef<HTMLDivElement>(null)
const deckRef = useRef<Reveal.Api>(null); // keep deck instance in a ref
useEffect(() => {
const isInitializing = deckDivRef.current?.classList.contains("reveal");
if (isInitializing) return; // escape useEffect if deckDiv already has "reveal" classes added
deckDivRef.current.current!.classList.add("reveal"); // add "reveal" class
deckRef.current = new Reveal(deckDivRef.current!, {
backgroundTransition: "slide",
transition: "slide",
});
deckRef.current.initialize().then(() => {
// good place for event handlers and plugin setups
};
return () => {
// code to run on component unmount goes here
if (!deckRef.current) return;
try {
deckRef.current!.destroy();
} catch (e) {
console.warn("destroy call failed.");
}
};
}, []); // only launch useEffect at first render
return (
<div ref={deckDivRef }>
<div className="slides">
<section>Slide 1</section>
<section>Slide 2</section>
</div>
</div>
);
}
Note that I use the reference to the div
element in creating the Reveal
constructor and this is important if you want to add multiple decks to the App
.
I have created a package called react-reveal-slides for creating reveal presentations entirely in react. It takes care of pretty much everything in the last section for you and allows you to easily create presentations using react components including all of the contents of the presentation. You can make every slide completely dynamic, control the presentation state and more. You can also create multiple presentations.
import { RevealSlides } from "react-reveal-slides"
// Make sure reveal.js is installed with npm for the following imports to work
// Plugins
import RevealNotes from 'reveal.js/plugin/notes/notes';
import RevealZoom from 'reveal.js/plugin/zoom/zoom';
const timeDelta = 1000;
function App() {
const [firstSlideText, setFirstSlideText] = useState("Create dynamic Reveal.js slides")
const [presState, setPresState] = useState({"indexh": -1, "indexv": -1, "indexf": -1, "paused": false, "overview": false })
const [useCustomTheme] = useState(false);
const [controlsLayout] = useState<"edges" | "bottom-right" | undefined>("edges");
// The following is just to show that you can reconfigure and control the RevealSlides component and its react content
useEffect(() => {
if (!showIntro) return;
const timer = setTimeout(() => {
setTheme("none")
}, 3*timeDelta);
const timer2 = setTimeout(() => {
setFirstSlideText("Explore new possibilities thanks to the React framework and ecosystem")
}, 6*timeDelta);
const timer2a = setTimeout(() => {
setPresState({"indexh": 0, "indexv": 1, "indexf": 0, "paused": false, "overview": false });
}, 9*timeDelta);
const timer2b = setTimeout(() => {
setPresState({"indexh": 0, "indexv": 1, "indexf": 1, "paused": false, "overview": false });
}, 12*timeDelta);
const timer2c = setTimeout(() => {
setPresState({"indexh": 0, "indexv": 1, "indexf": 2, "paused": false, "overview": false });
}, 15*timeDelta);
return () => {
clearTimeout(timer);
clearTimeout(timer2);
clearTimeout(timer2a);
clearTimeout(timer2b);
clearTimeout(timer2c);
}
}, []);
return (
<RevealSlides
controlsLayout={controlsLayout}
controls={false}
presState={presState}
theme={theme}
plugins={[RevealZoom, RevealNotes]}
onStateChange={(state)=>console.log(state)}
>
<section key="0" data-background-color="#0c1821">
<section key="0-0">
<h2 style={{color: "#E7AD52", marginTop: "-0.5rem"}}>react-reveal-slides</h2>
<p key={firstSlideText} style={{animation: "fadeIn 500ms ease-in-out", height: "7rem"}}>{firstSlideText}</p>
</section>
<section key="0-1">
<ul>
<li className="fragment">Easily make presentation content dynamic</li>
<li className="fragment">Easily add presentations to React apps</li>
<li className="fragment">Embed React components inside presentations</li>
</ul>
</section>
</section>
<section key="1" data-background-color='#bf4f41'>
<section key="1-0">
<h2 style={{color: "#432534"}}>
Free reign over your presentation
</h2>
<p>This package makes no efforts to impead or restrict what you can do.</p>
</section>
<section key="1-1">
<p>Since React creates HTML DOM elements out of JSX, there should be no reason we cant just put JSX inside of our RevealSlides component instead of the HTML markup Reveal.js normally expects.</p>
</section>
<section key="1-2">
<p>Simply put, React already takes care of converting JSX into something Reveal.js can work with.</p>
<aside className="notes">
Shhh, these are your private notes 📝
</aside>
</section>
</section>
</section>
</RevealSlides>
)
The package is still in dev phase (although everything I just mentioned is working) and has not yet been published to npm
. This means you will have to install it using the repo.
Searched the open and closed issues, nobody has seemed to have done this or asked about this yet. Does anyone in the community have experience with using reveal.js in a react project or know a decent react wrapper for it? (I found some old react wrappers for reveal.js but most haven't seen any changes in the last 4+ years!)