Skip to content

Instantly share code, notes, and snippets.

@andrewserong
Created October 19, 2017 05:47
Show Gist options
  • Save andrewserong/799db253ad6340201ef5130f4daeaa0f to your computer and use it in GitHub Desktop.
Save andrewserong/799db253ad6340201ef5130f4daeaa0f to your computer and use it in GitHub Desktop.
An example Video JS component in React, based on the Video JS docs
import React from 'react';
import videojs from 'video.js';
import 'video.js/dist/video-js.css';
// video.js player from the docs: https://github.com/videojs/video.js/blob/master/docs/guides/react.md
class VideoPlayer extends React.Component {
componentDidMount() {
// instantiate Video.js
this.player = videojs(this.videoNode, this.props, function onPlayerReady() {
console.log('onPlayerReady', this)
});
}
// destroy player on unmount
componentWillUnmount() {
if (this.player) {
this.player.dispose();
}
}
componentWillReceiveProps(newProps) {
// When a user moves from one title to the next, the VideoPlayer component will not be unmounted,
// instead its properties will be updated with the details of the new video. In this case,
// we can update the src of the existing player with the new video URL.
if (this.player) {
this.player.src({
type: newProps.video.mime_type,
src: newProps.video.video_url
});
}
}
// wrap the player in a div with a `data-vjs-player` attribute
// so videojs won't create additional wrapper in the DOM
// see https://github.com/videojs/video.js/pull/3856
// use `ref` to give Video JS a reference to the video DOM element: https://reactjs.org/docs/refs-and-the-dom
render() {
return (
<div data-vjs-player>
<video ref={ node => this.videoNode = node } className="video-js"></video>
</div>
)
}
}
export default VideoPlayer;
@char0n
Copy link

char0n commented Nov 20, 2019

Anybody looking for hook alternative to this code, check out the following snippet:

import React, { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import videojs from 'video.js';

// eslint-disable-next-line import/prefer-default-export
const usePlayer = ({ src, controls, autoplay }) => {
  const options = {
    fill: true,
    fluid: true,
    preload: 'auto',
    html5: {
      hls: {
        enableLowInitialPlaylist: true,
        smoothQualityChange: true,
        overrideNative: true,
      },
    },
  };
  const videoRef = useRef(null);
  const [player, setPlayer] = useState(null);

  useEffect(() => {
    const vjsPlayer = videojs(videoRef.current, {
      ...options,
      controls,
      autoplay,
      sources: [src],
    });
    setPlayer(vjsPlayer);

    return () => {
      if (player !== null) {
        player.dispose();
      }
    };
  }, []);
  useEffect(() => {
    if (player !== null) {
      player.src({ src });
    }
  }, [src]);

  return videoRef;
};

const VideoPlayer = ({ src, controls, autoplay }) => {
  const playerRef = usePlayer({ src, controls, autoplay });

  return (
    <div data-vjs-player>
      <video ref={playerRef} className="video-js" />
    </div>
  );
};

VideoPlayer.propTypes = {
  src: PropTypes.string.isRequired,
  controls: PropTypes.bool,
  autoplay: PropTypes.bool,
};

VideoPlayer.defaultProps = {
  controls: true,
  autoplay: false,
};

export default VideoPlayer;

@shahabrashidi
Copy link

HI I am bit confusing how can i add my source url ?

@Axelcouty
Copy link

Axelcouty commented Apr 16, 2020

Hey @char0n your example almost work :p, thanks for this snippet !

There is a mistake there:

Player.propTypes = {
  src: PropTypes.string.isRequired,
  controls: PropTypes.bool,
  autoplay: PropTypes.bool,
};

Player.defaultProps = {
  controls: true,
  autoplay: false,
};

It should be VideoPlayer instead of Player. I think.

VideoPlayer.propTypes = {
  src: PropTypes.string.isRequired,
  controls: PropTypes.bool,
  autoplay: PropTypes.bool,
};

VideoPlayer.defaultProps = {
  controls: true,
  autoplay: false,
};

@shahabrashidi
Using the props :

<VideoPlayer  src=""/>

@char0n
Copy link

char0n commented Apr 16, 2020

Right @Axelcouty. Thank you for catching it up! Edited the snippet and fixed the incosistency.

@mikevb3
Copy link

mikevb3 commented May 18, 2020

Thank you @andrewserong and @char0n!

@vishwasnavadak
Copy link

Anybody looking for hook alternative to this code, check out the following snippet:

import React, { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import videojs from 'video.js';

// eslint-disable-next-line import/prefer-default-export
const usePlayer = ({ src, controls, autoplay }) => {
  const options = {
    fill: true,
    fluid: true,
    preload: 'auto',
    html5: {
      hls: {
        enableLowInitialPlaylist: true,
        smoothQualityChange: true,
        overrideNative: true,
      },
    },
  };
  const videoRef = useRef(null);
  const [player, setPlayer] = useState(null);

  useEffect(() => {
    const vjsPlayer = videojs(videoRef.current, {
      ...options,
      controls,
      autoplay,
      sources: [src],
    });
    setPlayer(vjsPlayer);

    return () => {
      if (player !== null) {
        player.dispose();
      }
    };
  }, []);
  useEffect(() => {
    if (player !== null) {
      player.src({ src });
    }
  }, [src]);

  return videoRef;
};

const VideoPlayer = ({ src, controls, autoplay }) => {
  const playerRef = usePlayer({ src, controls, autoplay });

  return (
    <div data-vjs-player>
      <video ref={playerRef} className="video-js" />
    </div>
  );
};

VideoPlayer.propTypes = {
  src: PropTypes.string.isRequired,
  controls: PropTypes.bool,
  autoplay: PropTypes.bool,
};

VideoPlayer.defaultProps = {
  controls: true,
  autoplay: false,
};

export default VideoPlayer;

Thank you for this @char0n

@vazbloke
Copy link

In the above hooks example, I'm not sure player.dispose() ever gets called, since player would always be null. Replacing it with vjsPlayer would work.

import * as React from 'react';
import { useState, useEffect, useRef } from 'react';

import videojs from 'video.js';
import 'video.js/dist/video-js.min.css';

interface PlayerProps {
  src: string;
  type: string;
}

const options = {
  fill: true,
  fluid: true,
  responsive: true,
  preload: 'auto',
  controls: true,
};

export const VideoPlayer = ({ src = '', type = 'video/mp4' }: PlayerProps) => {
  const videoRef = useRef(null);
  const [player, setPlayer] = useState(null);

  useEffect(() => {
    const vjsPlayer = videojs(videoRef.current, options);
    setPlayer(vjsPlayer);

    return () => vjsPlayer.dispose();
  }, []);

  useEffect(() => {
    if (player !== null) {
      player.src({ src, type });
    }
  }, [src, type, player]);

  return (
    <div>
      {/* eslint-disable-next-line jsx-a11y/media-has-caption */}
      <video ref={videoRef} className="video-js">
        <p className="vjs-no-js">
          To view this video please enable JavaScript, and consider upgrading to
          a web browser that
          <a href="https://videojs.com/html5-video-support/">
            supports HTML5 video
          </a>
        </p>
      </video>
    </div>
  );
};

@samirph
Copy link

samirph commented Jul 3, 2020

In @char0n example, how do we handle events?

@xairoo
Copy link

xairoo commented Jul 23, 2020

In @char0n example, how do we handle events?

const vjsPlayer = videojs(
	videoRef.current,
	{
		...videoJsOptions,
	},
	function onPlayerReady() {
		this.pause();
	},
);

// Events:
vjsPlayer.on('seeking', function() {
	console.log('seeking');
});

@nikhil-sharma0
Copy link

nikhil-sharma0 commented Jul 23, 2020

@char0n @vazbloke @xairoo
I am encountering a problem while trying to fallback from hls to mp4. I want to have my primary source as m3u8 and secondary as mp4. So if the player doesn't find m3u8 file I want it to fallback to the next src and start playing the mp4 one. But my player doesn't stop trying to fetch the m3u8 file even when it's not there.
I get this in my console VIDEOJS: WARN: Problem encountered with the current HLS playlist. Trying again since it is the only playlist.
I am replacing .mp4 with /master.m3u8 because that's where my m3u8 file will be in CDN.

I am trying this in React with video.js v7.8.4 and this is my code:

import React, { useEffect, useRef, useState } from "react";
import PropTypes from "prop-types";
import videojs from "video.js";
import "video.js/dist/video-js.min.css";

const options = {
fill: true,
fluid: true,
responsive: true,
preload: "auto",
controls: true,
html5: {
  vhs: {
   enableLowInitialPlaylist: false,
    smoothQualityChange: true,
    overrideNative: true
  }
},
playbackRates: [0.25, 0.5],
sourceOrder: true
};

export default function VideoViewer(props) {
  const { src } = props;
  const classes = useStyles();
  const videoRef = useRef(null);
  const [player, setPlayer] = useState(null);

  useEffect(() => {
    const videoPlayer = videojs(videoRef.current, options);
    setPlayer(videoPlayer);

    return () => videoPlayer.dispose();
  }, []);

  useEffect(() => {
    if (player !== null) {
      player.src([
        {
          src: src.replace(".mp4", "/master.m3u8"),
          type: "application/x-mpegURL"
        },
        {
          src,
          type: "video/mp4"
        }
      ]);
    }
  }, [src, player]);

  return (
    <div data-vjs-player>
      <video ref={videoRef} className="video-js vjs-big-play-centered" />
    </div>
  );
}

VideoViewer.propTypes = {
  src: PropTypes.string.isRequired
};

I am pretty new to this so I might be making some mistake here, feel free to correct me if I missed anything or did something wrong.
Any kind of help is appreciated, thanks!

@Nitin-jha2016
Copy link

HoW do i add Modal inside this so that when user pause a modal shows which can show pause, play or rewind any thing options as modal on top of screen of current player

@irzhywau
Copy link

irzhywau commented Oct 7, 2020

I'm using almost the same code with a youtube video and just did not work, mp4 source seems fine but nto youtube link, any idea guys?

@ducxinh
Copy link

ducxinh commented Oct 23, 2020

Hey guys
Could you please tell me how to integrate with videojs-quality-selector?

@sanjib-dev
Copy link

sanjib-dev commented Oct 30, 2020

Hey guys
Could you please tell me how to integrate with videojs-quality-selector?

Step 1:
install below plugins:
videojs-contrib-quality-levels
videojs-hls-quality-selector
Step 2:
import this in your videojs component

import videoJsContribQualityLevels from 'videojs-contrib-quality-levels'
import videojsHlsQualitySelector from 'videojs-hls-quality-selector'

Step 3:
register plugins after the import statement:

videojs.registerPlugin('qualityLevel', videoJsContribQualityLevels)
videojs.registerPlugin('hlsQualitySelector', videojsHlsQualitySelector)

Step 4:
add this plugin in options
const options = { fill: true, fluid: true, preload: 'auto', html5: { hls: { enableLowInitialPlaylist: true, smoothQualityChange: true, overrideNative: true, }, }, plugins: { qualityLevel: {}, hlsQualitySelector: { displayCurrentQuality: true }, }

This is what I did.
Videojs version is : 7.9.7

@ItZmEkAtHiR
Copy link

Can you share the full code with "videojs-hls-quality-selector"

@sahilkashyap64
Copy link

I'm using almost the same code with a youtube video and just did not work, mp4 source seems fine but nto youtube link, any idea guys?

Have you added youtube.min.js video.js player(corejs) which plays links dynamically https://sahilkashyap64.github.io/hls/index5.html

@HIR666
Copy link

HIR666 commented May 3, 2021

Anybody looking for hook alternative to this code, check out the following snippet:

import React, { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import videojs from 'video.js';

// eslint-disable-next-line import/prefer-default-export
const usePlayer = ({ src, controls, autoplay }) => {
  const options = {
    fill: true,
    fluid: true,
    preload: 'auto',
    html5: {
      hls: {
        enableLowInitialPlaylist: true,
        smoothQualityChange: true,
        overrideNative: true,
      },
    },
  };
  const videoRef = useRef(null);
  const [player, setPlayer] = useState(null);

  useEffect(() => {
    const vjsPlayer = videojs(videoRef.current, {
      ...options,
      controls,
      autoplay,
      sources: [src],
    });
    setPlayer(vjsPlayer);

    return () => {
      if (player !== null) {
        player.dispose();
      }
    };
  }, []);
  useEffect(() => {
    if (player !== null) {
      player.src({ src });
    }
  }, [src]);

  return videoRef;
};

const VideoPlayer = ({ src, controls, autoplay }) => {
  const playerRef = usePlayer({ src, controls, autoplay });

  return (
    <div data-vjs-player>
      <video ref={playerRef} className="video-js" />
    </div>
  );
};

VideoPlayer.propTypes = {
  src: PropTypes.string.isRequired,
  controls: PropTypes.bool,
  autoplay: PropTypes.bool,
};

VideoPlayer.defaultProps = {
  controls: true,
  autoplay: false,
};

export default VideoPlayer;

how do i add text tracks to this? thanks :)

@himalreddy
Copy link

How to add https://github.com/streamroot/videojs-hlsjs-plugin plugin in the above code.

@rahulbansal16
Copy link

The styling files is not getting imported by using hooks component mentioned above.

@rahulbansal16
Copy link

The solution was to add the css download in the head file

<link href="//vjs.zencdn.net/7.10.2/video-js.min.css" rel="stylesheet">

@geosigno
Copy link

geosigno commented Jul 8, 2021

@char0n @vazbloke @xairoo
I am encountering a problem while trying to fallback from hls to mp4. I want to have my primary source as m3u8 and secondary as mp4. So if the player doesn't find m3u8 file I want it to fallback to the next src and start playing the mp4 one. But my player doesn't stop trying to fetch the m3u8 file even when it's not there.
I get this in my console VIDEOJS: WARN: Problem encountered with the current HLS playlist. Trying again since it is the only playlist.
I am replacing .mp4 with /master.m3u8 because that's where my m3u8 file will be in CDN.

I am trying this in React with video.js v7.8.4 and this is my code:

import React, { useEffect, useRef, useState } from "react";
import PropTypes from "prop-types";
import videojs from "video.js";
import "video.js/dist/video-js.min.css";

const options = {
fill: true,
fluid: true,
responsive: true,
preload: "auto",
controls: true,
html5: {
  vhs: {
   enableLowInitialPlaylist: false,
    smoothQualityChange: true,
    overrideNative: true
  }
},
playbackRates: [0.25, 0.5],
sourceOrder: true
};

export default function VideoViewer(props) {
  const { src } = props;
  const classes = useStyles();
  const videoRef = useRef(null);
  const [player, setPlayer] = useState(null);

  useEffect(() => {
    const videoPlayer = videojs(videoRef.current, options);
    setPlayer(videoPlayer);

    return () => videoPlayer.dispose();
  }, []);

  useEffect(() => {
    if (player !== null) {
      player.src([
        {
          src: src.replace(".mp4", "/master.m3u8"),
          type: "application/x-mpegURL"
        },
        {
          src,
          type: "video/mp4"
        }
      ]);
    }
  }, [src, player]);

  return (
    <div data-vjs-player>
      <video ref={videoRef} className="video-js vjs-big-play-centered" />
    </div>
  );
}

VideoViewer.propTypes = {
  src: PropTypes.string.isRequired
};

I am pretty new to this so I might be making some mistake here, feel free to correct me if I missed anything or did something wrong.
Any kind of help is appreciated, thanks!

I am having a similar issue, did you succeed to dynamically change the src?

@char0n
Copy link

char0n commented Jul 8, 2021

@geosigno not really sure how to fix this. It seems that this is comming directly from video.js and is not related with the React component I've provided.

More info about possible workarounds:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment