How to Implement Custom Controls with React-Player▶️: A Step-by-Step 🪜Guide 📖(with Picture-in-Picture Mode)

Israel
5 min readOct 4, 2024

The only guide you would find out there 🥴…

Building a custom video player for a web application is a powerful way to enhance user experience, especially with features like custom controls and fullscreen mode. Adding Picture-in-Picture (PiP) functionality is another feature that can make your video player even more engaging, allowing users to continue watching the video while navigating away from the main page.

In this article, we’ll extend our custom React video player by implementing Picture-in-Picture mode alongside other controls like play/pause, volume, fullscreen, and keyboard shortcuts.

Prerequisites:

Basic knowledge of javascript, react, and nextjs.

1. Installing Required Packages

First, ensure you have react-player installed in your project.

npm install react-player

2. Setting Up Basic Video Player

The foundation of our custom video player starts with the ReactPlayer component. Here's a simple version:

import React, { useState, useRef } from 'react';
import ReactPlayer from 'react-player';
import { Box } from 'styled-system';

export const VideoPlayer = ({ source }) => {
const playerRef = useRef(null);
const [playing, setPlaying] = useState(false);

const handlePlayPause = () => {
setPlaying((prev) => !prev);
};

return (
<Box w="100%">
<ReactPlayer
ref={playerRef}
url={source}
playing={playing}
controls={false}
width="100%"
height="auto"
/>
<button onClick={handlePlayPause}>
{playing ? 'Pause' : 'Play'}
</button>
</Box>
);
};

This code sets up a basic video player where the user can play and pause the video.

3. Adding Custom Controls

Next, we’ll implement custom controls for play/pause, forward, backward, and volume.

import React, { useCallback, useState, useRef } from 'react';
import ReactPlayer from 'react-player';
import { Box, Flex, Button, Slider } from 'styled-system';

export const VideoPlayer = ({ source }) => {
const playerRef = useRef(null);
const [playing, setPlaying] = useState(false);
const [volume, setVolume] = useState(0.5);
const [played, setPlayed] = useState(0);

const handlePlayPause = useCallback(() => {
setPlaying(prev => !prev);
}, []);

const seekForward = () => {
playerRef.current.seekTo(playerRef.current.getCurrentTime() + 10);
};

const seekBackward = () => {
playerRef.current.seekTo(playerRef.current.getCurrentTime() - 10);
};

return (
<Box w="100%">
<ReactPlayer
ref={playerRef}
url={source}
playing={playing}
volume={volume}
onProgress={({ played }) => setPlayed(played)}
controls={false}
width="100%"
height="auto"
/>
<Flex justify="space-between">
<Button onClick={seekBackward}>Rewind</Button>
<Button onClick={handlePlayPause}>
{playing ? 'Pause' : 'Play'}
</Button>
<Button onClick={seekForward}>Forward</Button>
</Flex>
{/* Volume Control with Input */}
<Box mt="2">
<label htmlFor="volume">Volume: </label>
<input
id="volume"
type="range"
min="0"
max="1"
step="0.01"
value={volume}
onChange={(e) => setVolume(parseFloat(e.target.value))}
/>
</Box>

{/* Progress Control with Input */}
<Box mt="2">
<label htmlFor="progress">Progress: </label>
<input
id="progress"
type="range"
min="0"
max="1"
step="0.01"
value={played}
onChange={(e) => {
const newValue = parseFloat(e.target.value);
setPlayed(newValue);
playerRef.current.seekTo(newValue);
}}
/>
</Box>
</Box>
);
};

4. Implementing Fullscreen Mode

Fullscreen mode can be activated using the native browser APIs. Here’s how you can add a fullscreen toggle button:

const [isFullscreen, setIsFullscreen] = useState(false);
const videoRef = useRef(null);

const toggleFullscreen = () => {
if (!isFullscreen) {
videoRef.current.requestFullscreen();
} else {
document.exitFullscreen();
}
setIsFullscreen(prev => !prev);
};

return (
<Box ref={videoRef} w="100%">
{/* Video player content */}
<Button onClick={toggleFullscreen}>
{isFullscreen ? 'Exit Fullscreen' : 'Fullscreen'}
</Button>
</Box>
);

5. Handling Keyboard Shortcuts

You can add keyboard shortcuts to control the video. For example, the space bar will toggle play/pause, and the arrow keys can seek forward or backward.

useEffect(() => {
const handleKeyDown = (event) => {
switch (event.key) {
case ' ':
handlePlayPause();
event.preventDefault();
break;
case 'ArrowRight':
seekForward();
break;
case 'ArrowLeft':
seekBackward();
break;
case 'f':
toggleFullscreen();
break;
default:
break;
}
};

window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [handlePlayPause, seekForward, seekBackward, toggleFullscreen]);

6. Adding Picture-in-Picture Mode

Picture-in-Picture (PiP) mode allows users to continue watching the video in a small, movable window while interacting with other parts of the webpage. Implementing this in the browser is straightforward using the native requestPictureInPicture() API.

Here’s how you can add PiP functionality to your player:

const [isPip, setIsPip] = useState(false);

const togglePictureInPicture = async () => {
if (playerRef.current && !isPip) {
try {
await playerRef.current.getInternalPlayer().requestPictureInPicture();
setIsPip(true);
} catch (error) {
console.error('Picture-in-Picture failed:', error);
}
} else if (document.pictureInPictureElement) {
await document.exitPictureInPicture();
setIsPip(false);
}
};

return (
<Box w="100%">
<ReactPlayer
ref={playerRef}
url={source}
playing={playing}
volume={volume}
onProgress={({ played }) => setPlayed(played)}
controls={false}
width="100%"
height="auto"
/>
<Button onClick={togglePictureInPicture}>
{isPip ? 'Exit PiP' : 'Picture-in-Picture'}
</Button>
</Box>
);

Here, we use the requestPictureInPicture() method to activate PiP mode. If PiP is active, the button changes to "Exit PiP" and calls document.exitPictureInPicture() to deactivate the mode.

To integrate the custom VideoPlayer component into your React project, you can easily pass a video source as a prop to the component. This allows dynamic video loading from various sources, such as URLs or file paths.

Example: Using the VideoPlayer Component in a Project

For instance, if you have an event object that contains a video recording URL, you can utilize the VideoPlayer component like this:

<VideoPlayer source={video.source} />

In this case, video.source points to the video URL dynamically retrieved from your data. The VideoPlayer component will render the video using the custom controls we implemented earlier, including play/pause, seek, volume control, Picture-in-Picture (PiP), and progress tracking.

By passing in the source dynamically, you ensure that your VideoPlayer It is reusable across various application parts, making it ideal for projects requiring multiple video instances.

7. Conclusion

Customizing the video player with react-player provides endless possibilities to enhance the user experience. By integrating features like play/pause, fullscreen mode, and Picture-in-Picture, you can create a modern video player that caters to various user needs.

Key features discussed in this guide include:

  • Play/Pause control
  • Rewind/Forward functionality
  • Volume control
  • Fullscreen toggle
  • Picture-in-Picture mode
  • Keyboard accessibility

This step-by-step approach will help you implement a feature-rich video player, improving the engagement and accessibility of your application.

Find this article helpful and interesting, don’t hesitate to share.
In subsequent articles, I’ll add more controls and functionality, and comment on what controls you would like to see.

--

--

Israel
Israel

Written by Israel

I'm Isreal a Frontend Engineer with 4+ experience in the space . My love to profer solutions led me to being a technical writer. I hope to make +ve impact here.

Responses (1)