Video events trigger on a Next.js Page

William Imoh

Interactivity is at the backbone of modern web pages. This includes collecting information on user interaction, handling form submissions, and managing interactivity on media assets.

This post discusses how to handle user interface interactions with events from a Video on a Next.js web page.

Next.js is a modern React.js framework for building high-performance web applications.

Try it out

We completed this project in CodeSandbox. You can fork the CodeSandbox project or run the code to get started quickly.

Prerequisites & Installation

For this post, knowledge of JavaScript and React.js is required. The knowledge of Next.js isn’t mandatory, but preferred.

To create a new project, we use the create next-app command to scaffold a new project. Using yarn, we run:

1yarn create next-app

A prompt appears to enter the project name, which we do to complete the project creation.

Alternatively, we could use NPX with the command:

1npx create-next-app

The boilerplate project scaffolds a landing page with styling using CSS modules.

For this project, we’ll use the Cloudinary-React package to render a video player whose video source is a video stored on Cloudinary. The video is delivered via an optimized content delivery network (CDN), and can be modified using Cloudinary’s robust transformation features.

We install the cloudinary-react package using yarn:

1yarn add cloudinary-react

Video player utilization

The cloudinary-react package exports two named components, Video and CloudinaryContext, which we’ll use to render the video player.

On the home page whose component is defined in pages/index.js, we import and render the video player with:

1import { Video, CloudinaryContext, Transformation } from "cloudinary-react";
2 export default function IndexPage() {
3 return (
4 <div>
5 Hello World.{" "}
6 <CloudinaryContext cloudName="chuloo">
7 <Video publicId="video-blog/cat" controls muted width="500px"/>
8 </CloudinaryContext>
9 </div>
10 );
11 }

CloudinaryContext receives props of any data we would like to make available to the Cloudinary child components. In this case, we passed our Cloudinary cloud name. You can obtain yours by creating an account on Cloudinary.

We use the Video component to render the video player with a width of 500px. We also specified the video’s public ID, which is stored on Cloudinary. Lastly, we included props that give the video player controls and make it muted on render.

The video player currently looks like this:

The cloudinary-react video player is a wrapper on the traditional HTML5 video element with enhanced properties. It provides an innerRef prop that grants us access to the underlying video element’s methods and properties.

With this, we will use React’s useRef() hook to reference the video element.

Video events handling

With the Video component’s ref, we’ll be able to utilize the onpause and onended events along with all other video events and properties. We create a ref and assign it to the innerRef prop of the Video with:

1import { Video, CloudinaryContext, Transformation } from "cloudinary-react";
2 import { useRef } from "react";
3 export default function IndexPage() {
4 const videoRef = useRef();
5 return (
6 <div>
7 Hello World.{" "}
8 <CloudinaryContext cloudName="chuloo">
9 <Video publicId="video-blog/cat" controls muted width="500px"
10 innerRef={videoRef}/>
11 </CloudinaryContext>
12 </div>
13 );
14 }

With the ref in place, we will handle actions when video events are triggered. We assign two functions, which we call when the video is paused, and the video ends. We do this in a useEffect hook with:

1import { useRef, useEffect, useState } from "react";
2 export default function IndexPage() {
3 const [videoEnded, setVideoEnded] = useState(undefined)
4 const videoRef = useRef()
5 useEffect(() => {
6 const video = videoRef.current;
7 video.onpause = () => {
8 // logic goes in here
9 }
10 video.onended = () => {
11 setVideoEnded(true)
12 // logic goes in here
13 }
14 }, [])
15 return (
16 // render component here
17 );
18 }

In the imported useEffect hook, we created a function that triggers when the video is paused or ended.

When the video ends, we store a true boolean value in the component’s state. With this state value, we can handle multiple UI updates. These interactions include:

  • Toasts to display status updates
  • Modals to communicate important information
  • Ads
  • Call to action buttons when a video ends
  • Handle navigation on specific video events
  • Fetch or post data

The interactivity opportunities are boundless.

We will be rendering a timed toast component that fires when the video is paused or ends for this project. We use react-hot-toast for this.

React-hot-toast is a lightweight toast package that is seamless to use. We install it using yarn with the command:

1yarn add react-hot-toast

In the .onpause and .onended functions, we trigger success toasts using the toast functions with:

1import { toast, Toaster } from "react-hot-toast";
2 export default function IndexPage() {
3 // component logic goes in here
4
5 useEffect(() => {
6 const video = videoRef.current;
7 video.onpause = () => {
8 toast.success("The video is paused");
9 };
10 video.onended = () => {
11 setVideoEnded(true);
12 toast.success("The video is ended");
13 };
14 }, []);
15
16 return (
17 // rendered component goes in here
18 );
19 }

To see the toasts, we render the imported Toaster component anywhere in the app.

1import { Video, CloudinaryContext, Transformation } from "cloudinary-react";
2 import { useRef, useEffect, useState } from "react";
3 import { toast, Toaster } from "react-hot-toast";
4 export default function IndexPage() {
5 // component logic definitions go in here
6
7 return (
8 <div>
9 <Toaster />
10 Hello World.{" "}
11 <CloudinaryContext cloudName="chuloo">
12 <Video
13 publicId="video-blog/cat"
14 controls
15 muted
16 width="500px"
17 innerRef={videoRef}
18 />
19 </CloudinaryContext>
20 </div>
21 );
22 }

Here’s what the final video looks like with the toasts on video events:

Conclusion

This post discussed using a video player in a Next.js app, hook into the video element’s native properties, events, and methods using useRef. We also handled UI changes on the page when a user fires video events.

Furthermore, you can handle data fetching and sending, or Ad display on your Next.js page using video events.

You may find these resources useful:

William Imoh

Creating tech solutions and talking about them

William is a developer, developer advocate, and product manager. When he's not working on technology, he's organizing game nights, making drinks, or playing basketball.