Building a Youtube-Style Post-Video Overlay CTA

Banner for a MediaJam post

Emma Alder

When you watch a YouTube video, at the end, some cards appear on the screen suggesting other videos to watch. This article is going to walk through managing video events to create a similar experience for videos in React.js.


We completed this project in a Codesandbox and it's also available on GitHub. Fork it to run the project or get started quickly.

1<CodeSandbox id="building-youtube-style-post-video-overlay-cta-2ylnu" title="Building YouTube-style post-video overlay CTA"/>

GitHub URL:


To follow through with this article, we need to have an understanding of JavaScript and React.js.

Project Setup

Node and its package manager npm are required to initialize a new project.

Using npm would require that we download the package to our computer before using it, but npm comes with an executor called npx. You can find more information on using npx here.

npx stands for Node Package Execute. It allows us to execute any package that we want from the npm registry without installing it.

To install Node, we go to the Nodejs website, download the Node version for our Operating system and follow the installation instructions. After we successfully installed the software, we should be able to verify Node.js by checking the installed version from the command line using the command below:

1node -v
2 v14.7.1

The result shows the version of Node.js we installed on our computer. In the above example, we have node at version 14.7.1.

Creating the application

create-react-app is the package we would use to generate a new react application. It is the official node package for creating react applications.

We use the command below to create a new application:

1npx create-react-app youtube-overlay-app

The above command will create a new React.js application called youtube-overlay-app.

Installing Cloudinary

Cloudinary provides a rich media management experience enabling users to upload, store, manage, manipulate, and deliver images and video for websites and applications.

Due to the performance benefits of serving images from an optimized Content Delivery Network, we’ll use images stored on Cloudinary. We’ll utilize the Cloudinary-react package to render Cloudinary videos on a page. We Install the cloudinary-react package in the project using npm by running the following command in the project’s root directory:

1npm i cloudinary-react

With installations completed, we’ll start the react application using the command below:

1npm start

Once run, the command spins up a local development server which we can access on http://localhost:3000.

Rendering the parent video component

In the “App.js” file in the “src” folder, we clear all the boilerplate code and then replace it with the code below that renders a Cloudinary video.

1import { Video, CloudinaryContext } from "cloudinary-react";
3 export default function App() {
4 return (
5 <div className="App">
6 <div className="video-area">
7 <CloudinaryContext cloudName="chukwutosin">
8 <Video
9 publicId="samples/elephants"
10 controls
11 muted
12 width="500px"
13 />
14 </CloudinaryContext>
15 </div>
16 </div>
17 );
18 }

First, we imported the Video component that renders the video onto the page and CloudinaryContext. This component takes props of any data we want to make available to its child Cloudinary components.

The cloudName prop is your Cloudinary cloud name which you can get from your Cloudinary dashboard once you create a Cloudinary Account.

Cloudinary offers a great free tier which is enough for all our development needs.

We use the Video component to render a 500px wide video player , having controls, and is muted on playback. We then pass a prop called publicId, which is the public ID of the video you want to render. A public ID is a unique identifier for a media asset stored on Cloudinary.

The rendered video should look like this:

Accessing the methods and properties of the video

We can seamlessly access the methods and properties of the video element using the innerRef prop provided by the cloudinary-react package.

By creating a reference using React’s useRef() hook and assigning it to the innerRef property, we can utilize the properties and methods of the underlying video element. Subsequently, all the video element’s available properties and methods will be on the .current property of the ref.

We create a ref and assign it with:

1import { Video, CloudinaryContext } from "cloudinary-react";
2 import { useRef } from "react"; // change 1
4 export default function App() {
5 const videoRef = useRef(); // change 2
7 return (
8 <div className="App">
9 <div className="video-area">
10 <CloudinaryContext cloudName="chukwutosin">
11 <Video
12 publicId="samples/elephants"
13 controls
14 muted
15 width="500px"
16 innerRef={videoRef} // change 3
17 />
18 </CloudinaryContext>
19 </div>
20 </div>
21 );
22 }

Managing video events

When the video playback starts and ends, we listen to both events and trigger specific actions. Using React’s useEffect hook, we’ll update the state of the video player stored in the component’s state.

We create the state variable and the effects with:

1import { Video, CloudinaryContext } from "cloudinary-react";
2 import { useRef, useEffect, useState } from "react";
4 export default function App() {
5 const [videoEnded, setVideoEnded] = useState(false);
6 const videoRef = useRef();
8 useEffect(() => {
9 const video = videoRef.current;
10 video.onplay = () => {
11 setVideoEnded(false);
12 };
14 video.onended = () => {
15 setVideoEnded(true);
16 };
17 }, []);
18 return (
19 // render component here
20 );
21 }

First, we created a state variable to hold a value for when the video playback finishes.

We listen for the onended and onplay events of the video In the useEffect hook. Next, we update the videoEnded variable to true if the video completes playing and false if it resumes playing.

The videoEnded state variable will be used in a condition to render the video overlay.

Applying the overlay effect

We start by creating a component to house the overlay that we would show. Next, we create a new file called ‘Card.js’ in src and paste it into the code below.

1const data = [
2 {
3 alt: "bike",
4 link: "",
5 thumbnail:
6 ""
7 },
8 {
9 alt: "sheep",
10 link: "",
11 thumbnail:
12 ""
13 },
14 {
15 alt: "kitten-playing",
16 link: "",
17 thumbnail:
18 ""
19 },
20 {
21 alt: "reindeer",
22 link: "",
23 thumbnail:
24 ""
25 }
26 ];
28 const Card = () => (
29 <div className="card">
30 {, index) => (
31 <span className="column">
32 <a key={index} href={}>
33 <img alt={element.alt} src={element.thumbnail} />
34 </a>
35 </span>
36 ))}
37 </div>
38 );
39 export default Card;

data is an array of Objects with information for each card we want to overlay on the video element. Then, we made a functional component where we looped through each object in the data array and rendered a thumbnail image wrapped in a link.

We would need to add a stylesheet to customize the overlay on the video element. We do this by deleting the boilerplate styles in the index.css file and adding the following styles:

1.App {
2 font-family: Cambria, Cochin, Georgia, Times, "Times New Roman", serif;
3 text-align: center;
4 margin: 0;
5 padding: 0;
6 width: auto;
7 height: 100vh;
8 }
9 h3 {
10 color: rgb(46, 44, 44);
11 }
12 .video-area {
13 position: relative;
14 width: 500px;
15 margin: auto;
16 left: calc(50%200px);
17 }
18 .card {
19 left: 50%;
20 margin-left: -250px;
21 width: 500px;
22 position: absolute;
23 z-index: 1;
24 background: rgba(0, 0, 0, 0.6);
25 }
26 .column {
27 float: left;
28 text-align: center;
29 border: solid 1px rgb(180, 179, 179);
30 width: 45%;
31 margin: 2.3%;
32 height: 85px;
33 opacity: 1;
34 }
35 img {
36 height: 100%;
37 width: 100%;
38 }

After that, we import both files, ‘Card.js’ and ‘index.css’ into the ‘App.js’ file using:

1import "./index.css";
2 import Card from "./Card";
4 // the rest of the content goes here...

With the overlay cards completed, we will show the overlay or hide it depending on the state of the videoEnded variable, either true or false.

In App.js, we include a conditional render block to show or hide the <Card/> component.

1// code before return statement...
2 return (
3 <div className="App">
4 <h1>Building YouTube-style post-video overlay CTA</h1>
5 <h3>
6 This is a demo of how to build a simple clone of the YouTube post video
7 overlay
8 </h3>
9 {videoEnded && <Card />}
10 <div className="video-area">
11 <CloudinaryContext cloudName="chukwutosin">
12 <Video
13 publicId="samples/elephants"
14 controls
15 muted
16 width="500px"
17 innerRef={videoRef}
18 />
19 </CloudinaryContext>
20 </div>
21 </div>
22 );
23 }

With this, once the video playback completes, the videoEnded variable is set to true, and the cards overlays on the video. Each card can link to a CTA of our choice. Here’s what it looks like below.


This article addressed utilizing video events to create youtube-styled call-to-action cards on a video once playback ends. This technique is employed to increase conversion on a video's call to actions.


You may find these resources helpful.

Emma Alder

Technical Writer at

Technical writer at