Five Ways for Integrating the Cloudinary Video Player Into React Applications
Tags: cloudinary, react, video
Published: 2021-03-15 18:50:06

---
title: Five Ways for Integrating the Cloudinary Video Player Into React Applications
published: true
description: Learn the five ways in which to add the feature-rich Cloudinary Video Player to a React application through a function- or class-based component.
tags: Cloudinary, React, video
cover_image: https://res.cloudinary.com/cloudinary-marketing/image/upload/v1615832977/Web_Assets/blog/VidPL_React_Blog.png
---

[Code](https://github.com/rebeccapeltz/react-video-player-alternatives)  

[Demo](https://amazing-khorana-ae17ab.netlify.app/) 

[CodeSandbox](https://codesandbox.io/s/elastic-architecture-stlk6)

As of this writing [npm trends](https://www.npmtrends.com/react) reports over 10,000,000 weekly downloads of the React library—and no wonder. The [disruption created by Covid in 2020](https://www.weforum.org/agenda/2020/05/coronavirus-covid19-consumers-shopping-goods-economics-industry) is expected to persist, significantly upping the consumer demand for video as a means of enhancing experiences in online work, shopping, education, healthcare, social interactions, and entertainment. Hence the many questions on how to add a video player to a React application. This post shows you five ways in which to do that with the Cloudinary Video Player in a React component.

## Cloudinary Video Player

The Cloudinary Video Player is a feature-enhanced library that builds upon the popular, open-source [Video JS](https://videojs.com/) player. Like that player, the Cloudinary one is implemented on a webpage by rendering a `<video>` tag and then binding JavaScript and CSS functionality to the video DOM element. Also, Cloudinary has added the following features to the video-playing experience:

- [Transformations at the player or video level](https://cloudinary.com/documentation/video_player_api_reference)
- [Multiple players per page, including playlists created by metadata tagging and recommendations](https://cloudinary.com/documentation/video_player_playlists_recommendations)
- [Auto-formatting transformations, which ensure delivery in the best format for the viewing device or browser](https://cloudinary.com/documentation/video_manipulation_and_delivery#automatic_format_selection_f_auto)
- [Suggestion of adaptive bitrate streaming for videos that are longer than one minute to provide HTTP streaming and optimize resolution in unstable networks](https://cloudinary.com/documentation/video_manipulation_and_delivery#adaptive_bitrate_streaming_hls_and_mpeg_dash)
- [Customization](https://cloudinary.com/documentation/video_player_customization#video_player_visuals)
- [Monetization through interstitial ads](https://cloudinary.com/documentation/video_player_api_reference#ads_and_analytics)
- [Shoppable video](https://cloudinary.com/documentation/video_player_shoppable_videos) 
- [Analytics](https://cloudinary.com/documentation/video_player_events_analytics)
## Libraries in the Cloudinary Video Player
There are 2 Video Player libraries available: [**Standard** and **Light**](https://cloudinary.com/documentation/cloudinary_video_player#installation_and_setup). The Light library is smaller as it does not include the following features: Adaptive Bitrate Streaming, Video Ads and Shoppable videos. In this exercise, we'll be using the **Light** version.

We'll use the [Video Player](https://www.npmjs.com/package/cloudinary-video-player) library.


## React Components

With React, you can wrap functionality into a reusable component that renders HTML, CSS, and JavaScript.  Timing is important in many cases, however.  In the case of the Video Player, you can’t initialize it until a `<video>` tag has been rendered.  

This exercise leverages the following React hooks:

- **useState**
This function returns an immutable data value and a setter that can update it.
- **useEffect** 
This function is called when the component that holds it is rendered and then when stateful data within the function changes. useEffect helps determine that a component has been rendered and, in some respects, replaces the `componentDidMount` and `componentDidUpdate` life cycles in a Class function.
- **useContext** 
This function, which serves as a container for stateful values, can be passed to a component function to specify the state. useContext defines a provider function made up of useState functions. Sharing context between components is one way to implement inter-component communications. 

Also, custom hooks can refactor stateful code for reuse in many components. You’ll learn how to create a custom hook later in this post. 

## The Code
Here are the five components that manage state, logic, and event handling and with which you can host the Cloudinary Video Player:
1. A function-based component for an embedded, cloud-hosted video player
2. A class-based component
3. A function-based component with useHooks
4. A function-based component with Context
5. A function-based component with a custom hook called useCloudinaryVideoPlayer for use in any component that hosts video 

The external data for all the components in this exercise is defined in App.js. An object called video options contains the Cloudinary **cloudName** and **publicId**. You can pass other video options to the Video Player’s components. For simplicity, however, those two are the only ones that are required to identify the video you want to host.  

```JavaScript
const videoOptions = { cloudName: "demo", publicId: "race_road_car" };
```

For details on the options available for the Cloudinary Video Player, see [Video Player API Reference](https://cloudinary.com/documentation/video_player_api_reference).  

### Function-Based, Cloud-Hosted Video Player

On the Cloudinary site is a [demo page](https://studio.cloudinary.com/) on which you can experiment with Video Player options. Once you are satisfied with the features you have selected, the demo generates the JavaScript code or an embeddable `<iframe>` tag with the options set for hosting in the cloud. You’ll also get a URL with all of the settings, including **cloudName** and **publicId**.  

In the Apps.js file is the component JSX for rendering a card that contains the name of the component and the video player hosting the video specified in the video options. In this case, the component is named VideoPlayerCloudHosted. 

```jsx
{
  <div>
    <h2>Video Player Cloud Hosted</h2>
    <div>
      <VideoPlayerCloudHosted options={videoOptions} />
    </div>
  </div>
}
```

In the code below, the URL in the src attribute contains the cloud name, the public ID, and the options specified in the Video Player demo page. Also, the cloud name and the public ID here are passed to the component through props, which is passed from the parent. 


```JavaScript
import React from "react";

function VideoPlayerCloudHosted(props) {
  const url = `https://player.cloudinary.com/embed/?public_id=${props.options.publicId}&cloud_name=${props.options.cloudName}&player%5Bfluid%5D=true&player%5Bcontrols%5D=true&source%5Bsource_types%5D%5B0%5D=mp4`;

  return (
    <>
    <div className="iframe-container">
      <iframe className="responsive-iframe"
        title="Cloud Hosted Video Player"
        src={url}
        width="640"
        height="480"
        allow="autoplay; fullscreen; encrypted-media; picture-in-picture"
        allowFullScreen
        frameBorder="0"
      ></iframe>
      </div>
    </>
  );
}
export default VideoPlayerCloudHosted;
```  

All you’re doing in this functional component is rendering the `<iframe>` tag by passing along the videoOptions data.
 
```jsx
 {
<div className="video-card">
  <h2>Video Player Cloud Hosted</h2>
  <div className="vp">
    <VideoPlayerCloudHosted options={videoOptions} />
  </div>
</div>
}
```

Add CSS code for a responsive `<iframe>` tag:

```css
.iframe-container {
  position: relative;
  width: 100%;
  overflow: hidden;
  padding-top: 56.25%; /* 16:9 Aspect Ratio */
}

.responsive-iframe {
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  width: 100%;
  height: 100%;
  border: none;
}

```
An embedded video player has a few downsides, especially in production: you might not have as much control as you would like and, depending on network conditions, loading might be slower.  

### Class-Based Component  

In App.js, you import your class-based video player and render a card that uses the VideoPlayerClass component.

```jsx
{
  import VideoPlayerClass from "./VideoPlayerClass";

  <div className="video-card">
    <h2>Video Player in Class</h2>
    <div className="vp">
      <VideoPlayerClass />
    </div>
  </div>
}
```  

The Class component requires importing just one library,  **cloudinary-video-player**, to create a Cloudinary Video Player within Class. Also, import the CSS code to support the video player. You’ll see those imports in another example, which will ultimately justify the creation of a custom hook.  
 
```JavaScript
import React, { Component } from "react";
import "cloudinary-video-player/dist/cld-video-player.light.min";
import "cloudinary-video-player/dist/cld-video-player.light.min.css";

class VideoPlayerClass extends Component {
  videoPlayerInit = () => {
    window.cloudinary.videoPlayer("some-video", {
      cloud_name: this.props.options.cloudName,
      publicId: this.props.options.publicId,
      fluid: true,
      controls: true,
      preload: "auto",
      mute: true,
      autoplay: false
    });
  };
  componentDidMount() {
    this.videoPlayerInit();
  }
  render() {
    return (
      <>
          <video id="some-video" />
      </>
    );
  }
}
export default VideoPlayerClass;
``` 


The Video Player is then initialized with the specified options. This component implements the code required for initializing the video player and then binds the player to the DOM element, which is the first `<video>` tag with the class some-video in the videoPlayerInit function. The component then renders a `<video>` tag, after which the lifecycle function componentDidMount calls the videoPlayerInit function.

```JavaScript
class VideoPlayerClass extends Component {
  cld = () => {
    return new Cloudinary({ cloud_name: this.props.options.cloudName, secure: true });
  };
  videoPlayerInit = () => {
    const cld = this.cld();
    cld.videoPlayer("some-video", {
      publicId: this.props.options.publicId,
      fluid: true,
      controls: true,
      preload: "auto",
      mute: true,
      autoplay: false
    });
  };
  componentDidMount() {
    this.videoPlayerInit();
  }
  render() {
    return (
      <>
        <video id="some-video" />
      </>
    );
  }
}
export default VideoPlayerClass;
```

### Function Based Component

Now load the libraries and wait for the `<video>` tag to render in a function-based approach.
First,  render the component in App.js:

```jsx
{
  <div className="video-card">
    <h2>Video Player in Function</h2>
    <div className="vp">
      <VideoPlayerFunction options={videoOptions} />
    </div>
  </div>
}
```  

Next, import the libraries. Below is the same code in the class-based example:  

```JavaScript
import React, { useEffect } from "react";
import "cloudinary-video-player/dist/cld-video-player.light.min";
import "cloudinary-video-player/dist/cld-video-player.light.min.css";
```  

Finally, set up a functional component. The `videoPlayerInit` function looks the same as it did in the class-based approach. Note that props is passed to the function rather than being implicitly added to the class context, as in the class-based function.
Instead of relying on the `componentDidMount` function to notify you that the `<video>` tag has been rendered, you can determine that with the useEffect functional React hook and call the Video Player’s init function.
Keep in mind that the instructions of useEffect, which  is called any time the component re-renders, are executed as if the function was called in componentDidMound and `componentDidUpdate` in a class-based component. Since you don’t want to call initVideoPlayer except after the `<video>` tag is first rendered, be sure to guard against that scenario.  

```JavaScript
function VideoPlayerFunction(props) {
  const videoPlayerInit = () => {
    window.cloudinary.videoPlayer(document.querySelector(".fn-video"), {
      cloud_name: props.options.cloudName,
      publicId: props.options.publicId,
      fluid: true,
      controls: true,
      preload: "auto",
      mute: true,
      autoplay: false,
    });
  };

  useEffect(() => {
    return (videoPlayerInit(),[]);
  });
  return (
    <>
      <video className="fn-video" />
    </>
  );
}

export default VideoPlayerFunction;
``` 

### Function-Based Component With Context

The React hook useContext includes both `useState` and Provider functions. The convention for naming the provider is to give it the same name as the context object. In this case, you’ll have `VideoOptionsContext` and `VideoOptionsProvider`, which can share logic and state between components.
Start with creating `VideoOptionsContext` for holding and granting access to state. useState is a function that returns the current value of the state and a setter function that will set a new state value. You’ll capture the options `cloudName` and `publicId` in that context.  
The data is an object that contains those two video options. Create the context and name it **VideoOptionsContext**:  

```JavaScript
const video = { options: { cloudName: "demo", publicId: "race_road_car" } };  
export const VideoOptionsContext = createContext();  

``` 


Next, implement and export **VideoOptionsProvider**, which sets up the state for the options. Specify the default values for videoOptions, which are `cloudName` and `publicId`.

```JavaScript
import React, { createContext, useState } from "react";

const video = { options: { cloudName: "demo", publicId: "race_road_car" } };
export const VideoOptionsContext = createContext();

// This context provider is passed to any component requiring the context
export const VideoOptionsProvider = ({ children }) => {
  const [videoOptions, setVideoOptions] = useState(video.options);

  return (
    <VideoOptionsContext.Provider
      value={{
        videoOptions,
        setVideoOptions,
      }}
    >
      {children}
    </VideoOptionsContext.Provider>
  );
};
```  

To use this context in a functional component, import VideoOptionsContext into App.js and wrap the rendering of the VideoPlayerContext in this Context component:  

```JavaScript
import VideoPlayerContext from "./VideoPlayerContext";
import { VideoOptionsProvider } from "./VideoOptionsContext";

{
<div className="video-card">
  <h2>Video Player in Function with Context Provider</h2>
  <div className="vp">
    <VideoOptionsProvider>
      <VideoPlayerContext />
    </VideoOptionsProvider>
  </div>
</div>
}
``` 


**VideoPlayerContext** resembles **VideoPlayerFunction**, except that the former gets the options from context rather than from props.  
Note in the code that you import **VideoOptionsContext** and then pull options with the `useContext` hook. You can then reference the options as `options.videoOptions.cloudName` and `options.videoOptions.publicId`.  

```JavaScript
import React, { useEffect, useContext } from "react";
import { VideoOptionsContext } from "./VideoOptionsContext";
import "cloudinary-video-player/dist/cld-video-player.light.min";
import "cloudinary-video-player/dist/cld-video-player.light.min.css";

function VideoPlayerContext() {
  const options = useContext(VideoOptionsContext);

  const videoPlayerInit = () => {
    console.log("add video player JS");
    const player = window.cloudinary.videoPlayer(
      document.querySelector(".context-video"),
      {
        cloud_name:options.videoOptions.cloudName,
        publicId: options.videoOptions.publicId,
        fluid: true,
        controls: true,
        preload: "auto",
        mute: true,
        autoplay: false,
      }
    );

    player.on("loadedmetadata", (e) => {
      console.log("app detected", e);
    });
  };

  useEffect(() => {
    videoPlayerInit();  
  });
  console.log("calling fn render");
  return (
    <>
      <video className="context-video" />
    </>
  );
}

export default VideoPlayerContext;  
```

When you return `useEffect`, pass the second parameter as an empty array to avoid re-rendering

### Function-Based Component With Custom Hook 

You’ve now seen code duplicated across several component examples: library imports, the init video-player function, Cloudinary instantiation. And you might wonder, “How can I create a reactor?’ The answer is with a custom hook.
Since the convention for naming hooks is to prefix the functionality you’re capturing with use, create a useCloudinaryVideoPlayer hook. Also, since you’ve specified different classes for each of the samples, the hook must work with arguments and maintain the state for the following: 

- The cloud name
- The public ID
- The class name of the `<video>` tag, which serves as an element selector

Other than pulling the values of  those three variables, the code looks like the functional components you created earlier.   

```JavaScript
import { useState, useEffect } from 'react';
import "cloudinary-video-player/dist/cld-video-player.light.min";
import "cloudinary-video-player/dist/cld-video-player.light.min.css";

export const useCloudinaryVideoPlayer  = (props) =>{
  const [cloudName] = useState(props.cloudName);
  const [publicId] = useState(props.publicId);
  const [className] = useState(props.videoClass);
  const videoPlayerInit = () => {
    return window.cloudinary.videoPlayer(document.querySelector(`.${className}`), {
      cloud_name: cloudName,
      publicId: publicId,
      fluid: true,
      controls: true,
      preload: "auto",
      mute: true,
      autoplay: false    
    });
  };

  useEffect(() => {
    return(videoPlayerInit(),[]);
  });

  return "OK";
}``` 

You need not capture the setters because they won’t serve any purpose. App.js will continue to pass an object with only the cloud name and public ID.  

```jsx
<div className="video-card">
    <h2>Video Player Custom Hook</h2>
    <div className="vp">
      <VideoPlayerCustomHook options={videoOptions} />
    </div>
</div>
``` 

In the new **VideoPlayerCustomHooks** component, add the class name to the object that is passed to the useCloudinaryVideoPlayer hook.  

```JavaScript
import React from "react";
import { useCloudinaryVideoPlayer } from "./useCloudinaryVideoPlayer";

function VideoPlayerCustomHook(props) {
  const videoClass = "custom-video";
  useCloudinaryVideoPlayer({ ...props.options, videoClass: videoClass });

  return (
    <>
      <video className={videoClass} />
    </>
  );
}

export default VideoPlayerCustomHook;
```

Now that it can import the reusable code from the hook, the actual code for the Video Player is much simpler. Just add the video class to a new object that includes props and that serves as a parameter for the custom hook.


## Recommendations
There are many ways for creating a component with React and, therefore, one for hosting the Cloudinary Video player.
What’s the best way, you ask? For class-based components, you might want to use the example in this exercise, but you can also introduce function-based components into an app with class-based components. If you’ll be creating components that vary by certain data, consider leveraging a custom hook. Though probably not the best use case, custom hooks enable the use of context with a functional component. In general, the direction forward with React is through function-based components with hooks.

## Note:
May 2022. The cloudinary-video-player was updated to v1.9 and this removed the need to import cloudinary-core.  It changed the instantiation of the video player.  The current code in GitHub contains the updated Cloudinary Video Player code and instantiation. The markup code on this page uses the most recent libraries as well.