In modern web pages and platforms, human attention is earned, and even a slight reduction in the quality of user experience could lead to a significant drop in conversion. This can be said for media content, including videos and images.
How do you improve the user experience of video content on your application?
This post outlines how to autoplay video content when a user scrolls to the video on a Next.js page. We'll use the Cloudinary video player to render the video.
Cloudinary provides a robust media experience for any media type, covering image and video uploads, storage, and optimizations. With Cloudinary, you can store and render your video assets on a Next.js landing page.
Sandbox
You can find a complete version of the application on Codesandbox.
Prerequisites and Installation
To get the most out of this post, basic knowledge of JavaScript and React is required. An understanding of Next.js is also a necessity.
To get started, we install the Next.js CLI, and create a new Next application using NPX - a node package runner. You can also use yarn to install packages, and create a new project. Next.js ships with starters, and we'll use the Next.js defaulter starter in our application.
To get a Next.js project started, navigate to your desired directory, and run the command below:
1npx create-next-app
The command above will return an input, asking you for the project name. You can use your desired project name. Once done, it will create a Next.js project using the default starter.
We will use the Cloudinary video player shipped in the cloudinary-react
package. The video player is efficient because we can use a public ID that is a unique identifier of the video on Cloudinary. We'll use this ID to fetch our uploaded video asset, and build dynamic delivery. Also, robust video transformations from Cloudinary are available when using the video player.
Proceed to install the following dependencies using the command below:
1npm install Cloudinary-react react-intersection-observer
cloudinary-react
is a Software Development Kit (SDK) that allows us to seamlessly utilize Cloudinary’s image and video features in a React app.
react-intersection-observer tells us when an element enters or leaves a particular viewport. This is essential to know when our video player is in view.
Video player creation
First, we create a new directory called Videoplayer
in our app’s src/components directory. Next, we create a file called VideoPlayer.jsx
that will house the video player component. Then, we import the required dependencies using in the component with:
1// src/components/Videoplayer/VideoPlayer.jsx2 import React, { useEffect, useref } from "react";3 import { Video, CloudinaryContext } from "Cloudinary-react";4 import { useInView } from "react-intersection-observer";
Intersection observer API provides a way to asynchronously observe changes in the intersection of a target element with a parent element, or viewport, in the browser.
react-intersection-observer is a declarative wrapper component for Intersection observer API.
We create a function component for the video player, which takes in a video ref. The component will return a Cloudinary Video
component wrapped in a Cloudinary Context component. CloudinaryContext
allows you to define shared parameters that apply to all child elements.
Let’s add the snippet below to our VideoPlayer
component:
1const VidPlayer = React.memo(({ videoref }) => {2 return (3 <CloudinaryContext cloud_name="codedog">4 <Video5 publicId="videoplayer-demo"6 width="100%"7 controls8 innerref={videoref}9 />10 </CloudinaryContext>11 );12 });
In the code above, our component is wrapped in React.memo
.
[React.memo()](https://reactjs.org/docs/hooks-reference.html#usememo)
is a higher-order component (HOC) that takes a component as a prop, and returns a component, preventing a component from re-rendering if the props have not changed. React.memo()
implements memoization on React components, and prevents our Video player from rerendering even if its parent component rerenders.
The Cloudinary context specifies the cloud_name, with which we fetch media assets from Cloudinary. To obtain your cloud_name, you need to create an account on Cloudinary, and get the cloud name value from your Cloudinary dashboard.
We also specified options for our video player, including the publicId
of our video on Cloudinary.
Handling video interaction with intersection-observer
In src/components/VideoPlayer.jsx, we create a function component named VideoPlayer
. Inside it, we use the useInView
hook, imported from react-intersection-observer
to track a DOM element for when it gets in view.
1const VideoPlayer = () => {2 const videoref = useref();3 const { ref, inView } = useInView({4 threshold: 05 });6 useEffect(() => {7 if (inView === true) {8 videoref.current.play();9 }10 });11 return (12 <div ref={ref}>13 <VidPlayer videoref={videoref} />14 </div>15 );16 };17 export default VideoPlayer;
We assigned the ref
value, destructured from the useInView
hook, to the DOM element we want to monitor. In this case, it’s the div enclosing the rendered VidPlayer
component.
In the VideoPlayer
component’s useEffect
function, we play the video when in view.
The videoRef
reference is created using React’s useRef()
hook, and is assigned to the ref prop of video player. This ref value will be used to access the video player methods in our VideoPlayer
component. The HTML video element’s .play()
method is then used to play the video when it is in view.
refs
are references to objects. They can include DOM nodes, or values. useRef
returns a mutable object with a .current
property set to the initial value we passed to the hook. The code below should be added to our VideoPlayer.jsx
file.
Rendering the video player
In the site’s homepage, located in the src/pages
, we create a new file named index.jsx
. This is the home page of the website. In this file, we render the VideoPlayer
component in the parent Home
component along with other page content. The code snippet below should be copied into our newly created index.jsx
file in src/pages
directory.
1import Head from "next/head";2 import styles from "../styles/Home.module.css";3 import VideoPlayer from "../components/VideoPlayer/VideoPlayer";45 const Home = () => {6 return (7 <div className={styles.container}>8 <Head>9 <title>Video Player with Cloudinary</title>10 <link rel="icon" href="/favicon.ico" />11 </Head>12 <header className={styles.header}>13 <h1>Video Player</h1>14 </header>15 <main className={styles.main}>16 <section className={styles.intro}>17 <p>NOW EASILY</p>18 <h1 className={styles.title}>19 Render Your Videos with Cloudinary, <br /> And Observer Intersection20 Library.21 </h1>22 </section>
In the code block above, we created a Home
component, with the title
and icons for our landing page. Next, we add content and render the VideoPlayer
component. The code block below will be added to our index.jsx
file above.
1<section className={styles.img_wrapper}>2 <img src="/assets/video.svg" alt="illustration" />3 </section>4 </main>5 <section className={styles.description}>6 <p>7 There is no one who loves pain itself, who seeks after it, and wants to have it, simply because it is pain.8 There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain.9 </p>10 </section>11 <section className={styles.video_player}>12 <VideoPlayer />13 </section>14 </div>15 );16 };17 export default Home;
With this, the homepage should look like this:
Summary
In this post, we created a Next.js landing page, and made a video player using the Cloudinary Video player. We also added autoplay when in view, using react observer intersection. You can improve the application by utilizing other video player events.
You may check out the following useful resources: