Wassup guys, in this tutorial we are going to see how to build a custom video player in React. Let's jump straight into it!
Setup
Create a new react app
npx create-react-app custom-video-player
Cleanup
- Delete everything in the app div in
App.js
.
import "./App.css";
function App() {
return <div className="app"></div>;
}
export default App;
Delete everything in
App.css
in
index.css
add-
* {
margin: 0;
}
Create the UI for our Video player
Adding the Video
Inside the app div add a video tag with the src of your video, I am also going to add a className for styling-
<video
className="video"
src="https://res.cloudinary.com/dssvrf9oz/video/upload/v1635662987/pexels-pavel-danilyuk-5359634_1_gmixla.mp4"
></video>
Adding the controls of the videos
Below the video component, I will add this div which has some Svgs as icons. You can use direct Svgs like me or use an icon library for the icons :).
<div className="controlsContainer">
<div className="controls">
<img className="controlsIcon" alt="" src="/backward-5.svg" />
<img className="controlsIcon--small" alt="" src="/play.svg" />
<img className="controlsIcon" alt="" src="/forward-5.svg" />
</div>
</div>
Adding the progress bar for time
We are also going to create a progress bar that shows the current time and total time of the video.
<div className="timecontrols">
<p className="controlsTime">1:02</p>
<div className="time_progressbarContainer">
<div style={{ width: "40%" }} className="time_progressBar"></div>
</div>
<p className="controlsTime">2:05</p>
</div>
Forgive me for not having very good naming conventions in classes. I have forgotten how to name my classes because of Tailwind :P
Styling the UI
The video player looks very ugly right now, so let's style it. In App.css I am going to add some stylings-
/* Main Container */
.app {
display: flex;
flex-direction: column;
width: 100vw;
height: 100vh;
overflow: hidden;
}
/* Video */
.video {
width: 100vw;
height: 100vh;
}
/* Controls */
.controlsContainer {
display: flex;
flex-direction: column;
align-items: center;
width: 100vw;
background-color: transparent;
margin-top: -50vw;
padding: 0 40px;
z-index: 20;
}
.controls {
display: flex;
align-items: center;
justify-content: space-evenly;
padding-top: 18rem;
margin: auto;
}
.controlsIcon {
width: 40px;
height: 40px;
cursor: pointer;
margin-left: 10rem;
margin-right: 10rem;
}
.controlsIcon--small {
width: 32px;
height: 32px;
cursor: pointer;
margin-left: 10rem;
margin-right: 10rem;
}
/* The time controls section */
.timecontrols {
display: flex;
align-items: center;
justify-content: space-evenly;
position: absolute;
bottom: 4rem;
margin-left: 10vw;
}
.time_progressbarContainer {
background-color: gray;
border-radius: 15px;
width: 75vw;
height: 5px;
z-index: 30;
position: relative;
margin: 0 20px;
}
.time_progressBar {
border-radius: 15px;
background-color: indigo;
height: 100%;
}
.controlsTime {
color: white;
}
Now our video player would look like this-
Adding the logic to the player
To work on the functionalities we first need to attach a ref to the video with the useRef hook. So follow the steps given below:
- Create a ref like this-
const videoRef = useRef(null);
- Import the useRef hook from react
import { useRef } from "react";
- Attach it to the video
<video
ref={videoRef}
className="video"
src="https://res.cloudinary.com/dssvrf9oz/video/upload/v1635662987/pexels-pavel-danilyuk-5359634_1_gmixla.mp4"
></video>
Play and Pause functionality
For play and pause create a simple function, which takes an argument of control and based on the control it will play or pause the video-
const videoHandler = (control) => {
if (control === "play") {
videoRef.current.play();
} else if (control === "pause") {
videoRef.current.pause();
}
};
Now in the play.svg image, we will add an onClick function to start the video.
<img
onClick={() => videoHandler("play")}
className="controlsIcon--small"
alt=""
src="/play.svg"
/>
If you click on the icon the video will play!
Changing the icon based on the playing/paused state
To achieve this I am going to use the useState hook. Create a playing state like this-
const [playing, setPlaying] = useState(false);
In the const video handler function, we need to change the value onClick of them like this-
const videoHandler = (control) => {
if (control === "play") {
videoRef.current.play();
setPlaying(true);
} else if (control === "pause") {
videoRef.current.pause();
setPlaying(false);
}
};
Changing the icon
Where we have the play icon, now we will render it based on a condition with the help of a ternary operator -
{playing ? (
<img
onClick={() => videoHandler("pause")}
className="controlsIcon--small"
alt=""
src="/pause.svg"
/>
) : (
<img
onClick={() => videoHandler("play")}
className="controlsIcon--small"
alt=""
src="/play.svg"
/>
)}
Now, we can play and pause the video 🥳
Forwarding and reverting the video
I am going to create very simple functions for this-
const fastForward = () => {
videoRef.current.currentTime += 5;
};
const revert = () => {
videoRef.current.currentTime -= 5;
};
Now we will add these functions as onClick of the respective buttons.
Forward
<img
onClick={fastForward}
className="controlsIcon"
alt=""
src="/forward-5.svg"
/>
Revert
<img
onClick={revert}
className="controlsIcon"
alt=""
src="/backward-5.svg"
/>
Time progress bar
Get the length of the video
To get the length of the video, follow the following steps
- Give an id to the video component
<video
id="video1"
ref={videoRef}
className="video"
src="https://res.cloudinary.com/dssvrf9oz/video/upload/v1635662987/pexels-pavel-danilyuk-5359634_1_gmixla.mp4"
></video>
- Create a state to store the video length
const [videoTime, setVideoTime] = useState(0);
- Set the video time like this on the play of the video
if (control === "play") {
videoRef.current.play();
setPlaying(true);
var vid = document.getElementById("video1");
setVideoTime(vid.duration);
}
Now we can use the videoTime variable instead of hardcoded time. This string manipulation will make the time in a format like- 1:05
<p className="controlsTime">
{Math.floor(videoTime / 60) + ":" + ("0" + Math.floor(videoTime % 60)).slice(-2)}
</p>
Getting the current time of the video
To get the current time of video we will need to use a function that runs every second, so I am going to use window.setInterval for the same.
window.setInterval(function () {
setCurrentTime(videoRef.current?.currentTime);
}, 1000);
Now as always, we need to create a state to store the value-
const [currentTime, setCurrentTime] = useState(0);
Instead of the hard code value, we will use the variable
<p className="controlsTime">
{Math.floor(currentTime / 60) + ":" + ("0" + Math.floor(currentTime % 60)).slice(-2)}
</p>
Getting the progress and setting it to the progress bar
Create another state for progress-
const [progress, setProgress] = useState(0);
Now inside the window.setInterval function that we created, add another line-
setProgress((videoRef.current?.currentTime / videoTime) * 100);
The function would look like this now-
window.setInterval(function () {
setCurrentTime(videoRef.current?.currentTime);
setProgress((videoRef.current?.currentTime / videoTime) * 100);
}, 1000);
Our custom video player is now ready 🎉🎊
Useful links-
Top comments (3)
Try a
onTimeUpdate
event. I think it's better than creating Intervals updateOh cool. I will try it
Does not work. Do you have a demo version running somewhere