Embed a YouTube Player into a React App

Ifeoma Imoh

YouTube is a video-sharing platform owned by Google. The YouTube team offers an API that can be used to integrate the YouTube video playback experience into web applications. In this post, we will go through the process of embedding a YouTube player in a React application. We'll build a simple blog and use the react-youtube package to embed a YouTube player in it. react-youtube provides a React component that acts as an abstraction layer over the traditional YouTube IFrame Player API. Let's get started!

Here is a link to the demo on CodeSandbox.

Project Setup

Run this command in your terminal to create a simple React application:

1npx create-react-app youtube-player-demo

Next, navigate into the newly created directory and run the command below to install the dependencies we'll need for our app.

1npm install react-youtube react-router-dom@6

After the installation, create a folder called components in the /src directory to hold the application’s components.

Building the Blog

First, let's create a component called Home that will serve as the index page and hold a list of all blog posts in the application. Create a file called Home.js in the src/components folder and add the following to it:

1import React from 'react';
2 import { Link } from 'react-router-dom';
3 export default function Home({ data }) {
4 return (
5 <div className="cards">
6 {data.posts.map((post) => (
7 <div className="card" key={post.id}>
8 <div className="img-wrapper">
9 <img
10 src={`https://ytimg.googleusercontent.com/vi/${
11 post.url.split('v=')[1]
12 }/sddefault.jpg`}
13 alt={post.title}
14 ></img>
15 </div>
16 <div>
17 <h3>{post.title}</h3>
18 <p>{post.content.slice(0, 100) + '...'}</p>
19 <Link to={`/blog/${post.id}`}>Learn more...</Link>
20 </div>
21 </div>
22 ))}
23 </div>
24 );
25 }

The Home component accepts a data prop and renders a list of blog post cards. Each card has an automatically generated YouTube video thumbnail, details about the post, and a link to the post's page.

To keep things simple, we will be working with locally generated sample data. Create a file called data.json in the /src directory and populate it with the JSON data from this codeSandbox file.

The file holds a JSON object made up of an array of sample posts. Now replace the code in your App.js file with the following:

1import { Routes, Route, Link } from 'react-router-dom';
2 import Home from './components/Home';
3 import BlogPost from './components/BlogPost';
4 import data from './data.json';
5 import './App.css';
6 function App() {
7 return (
8 <div className="App">
9 <div>
10 <Link to="/">
11 <h1>React Blog</h1>
12 </Link>
13 </div>
14 <main>
15 <Routes>
16 <Route path="/" element={<Home data={data} />} />
17 <Route path="/blogpost/:id" element={<BlogPost data={data} />} />
18 </Routes>
19 </main>
20 </div>
21 );
22 }
23 export default App;

We imported the basic routing components, the previously created Home component, and the sample data in the code above. Then we configured our route inside the App component. Route accepts an element prop set to the Home component.

With that done, we need to wrap our application with the BrowserRouter component as required by React Router. Update your index.js file in the /src folder with the following:

1import React from 'react';
2 import ReactDOM from 'react-dom';
3 import './index.css';
4 import App from './App';
5 import { BrowserRouter } from 'react-router-dom';
6 ReactDOM.render(
7 <React.StrictMode>
8 <BrowserRouter>
9 <App />
10 </BrowserRouter>
11 </React.StrictMode>,
12 document.getElementById('root')
13 );

Let's add some styles to give the application a decent look. Add the following to your App.css file:

1.App {
2 width: 1024px;
3 max-width: 90%;
4 margin: 0 auto;
5 }
6 .App > div {
7 display: flex;
8 justify-content: center;
9 }
10 .App > div > a {
11 color: inherit;
12 text-decoration: none;
13 }
14 .cards {
15 display: grid;
16 grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
17 grid-gap: 2rem;
18 }
19 .card {
20 height: 20rem;
21 border: 1px solid rgba(0, 0, 0, 0.1);
22 }
23 .card > .img-wrapper {
24 height: 50%;
25 }
26 .card > .img-wrapper > img {
27 width: 100%;
28 height: 100%;
29 object-fit: cover;
30 }
31 .card > div:last-child {
32 padding: 0.5rem;
33 height: 50%;
34 }
35 .card > div > h3 {
36 text-align: center;
37 font-size: 0.9rem;
38 }
39 .card > div > p {
40 font-size: 0.8rem;
41 color: #555;
42 }
43 .card > div > a {
44 text-decoration: none;
45 padding: 0.2rem 0.5rem;
46 border: 1px solid #4cf;
47 color: inherit;
48 font-weight: 700;
49 font-size: 0.8rem;
50 display: inline-block;
51 }
52 .blog > h3 {
53 margin-bottom: 1rem;
54 }
55 .blog * {
56 width: 100%;
57 }
58 .btn {
59 display: inline;
60 padding: 0.5rem 2rem;
61 margin: 0.7rem 0;
62 width: auto;
63 background: #fff;
64 border: 1px solid #4cf;
65 cursor: pointer;
66 font-size: 1rem;
67 font-weight: 600;
68 }

Save the changes, and your application should now look like this:

Embed YouTube Player

The react-youtube package simplifies the integration of the YouTube player into React applications by providing a utility React component, <YouTube />, which accepts a videoId prop, an onReady prop, and some other optional props. The videoId prop gives the id of a particular YouTube video, while onReady hold a function that will be executed when the player is ready.

The following code illustrates the basic use of the component as well as its available props:

1<YouTube
2 videoId={string} // defaults -> null
3 id={string} // defaults -> null
4 className={string} // defaults -> null
5 containerClassName={string} // defaults -> ''
6 title={string} // defaults -> null
7 opts={obj} // defaults -> {}
8 onReady={func} // defaults -> noop
9 onPlay={func} // defaults -> noop
10 onPause={func} // defaults -> noop
11 onEnd={func} // defaults -> noop
12 onError={func} // defaults -> noop
13 onStateChange={func} // defaults -> noop
14 onPlaybackRateChange={func} // defaults -> noop
15 onPlaybackQualityChange={func} // defaults -> noop
16 />;

Open your App.js file and add a dynamic route to configure a dynamic path based on the id of each blog post.

1import BlogPost from './components/BlogPost';
2
3 //...
4
5 <Routes>
6 <Route path="/" element={<Home data={data} />} />
7 <Route path="/blogpost/:id" element={<BlogPost data={data} />} />
8 </Routes>

Now, let's create a BlogPost.js file in the src/components directory and add the following to it:

1import React from 'react';
2 import YouTube from 'react-youtube';
3 import { useParams } from 'react-router';
4 export default function BlogPost({ data }) {
5 const params = useParams();
6 const post = data.posts.find((dataItem) => dataItem.id === params.id);
7 const youtubeID = post.url.split('v=')[1];
8 const onReady = (e) => {
9 console.log(e.target);
10 };
11 return (
12 <div className="blog">
13 <h3>{post.title}</h3>
14 <p>{post.content}</p>
15 <div className="player">
16 <YouTube videoId={youtubeID} onReady={onReady} />
17 </div>
18 </div>
19 );
20 }

We imported the useParams hook, which is used to extract the params object from the dynamic path. Then we created a BlogPost component that accepts our sample local data as a prop. The data is then traversed to find the post object whose id matches the params id.

The video id of a YouTube video is usually contained in the video URL, mostly specified as a query parameter v. We performed some JavaScript string manipulation to extract the video id from the URL. The component then renders some content and, most importantly, the YouTube Player that takes the videoId and an onReady prop as props.

To view a blog post, save the changes and click on any blog post on the home page. You should see the embedded YouTube Player with an already loaded video.

Customizing the Player

The YouTube IFrame API provides a lot of freedom by defining many parameters that make the YouTube player more customizable. For example, you can easily define the player controls externally, set the video to play repeatedly, cause the player to play the loaded video automatically after rendering, etc.

However, the react-youtube package has an opts object prop that can help manage the video player's preferences. The opts prop has a playerVars nested object that allows you to utilize the usual YouTube IFrame API parameters and set a custom width and height for the player.

Let's look at some of the customization possibilities.

Define External Player Controls

Users can disable the default player controls and then create their logic by altering the API's state. For simplicity, we will include only the pause and play controls.

Update the code in your BlogPost.js file to look like this:

1import React, { useState } from 'react';
2 import YouTube from 'react-youtube';
3 import { useParams } from 'react-router';
4 export default function BlogPost({ data }) {
5 const [player, setPlayer] = useState(null);
6 const params = useParams();
7 const post = data.posts.find((dataItem) => dataItem.id === params.id);
8 const youtubeID = post.url.split('v=')[1];
9 const onReady = (e) => {
10 setPlayer(e.target);
11 };
12 const onPlayHandler = () => {
13 player.playVideo();
14 };
15 const onPauseHandler = () => {
16 player.pauseVideo();
17 };
18 return (
19 <div className="blog">
20 <h3>{post.title}</h3>
21 <p>{post.content}</p>
22 <div className="player">
23 <YouTube
24 videoId={youtubeID}
25 onReady={onReady}
26 opts={{
27 playerVars: {
28 controls: 0,
29 },
30 }}
31 />
32 </div>
33 <button onClick={onPlayHandler} className="btn">
34 Play
35 </button>
36 <button onClick={onPauseHandler} className="btn">
37 Pause
38 </button>
39 </div>
40 );
41 }

We created a new state variable called player to hold the player state in the onReady function when rendering the YouTube player component. We then use this player state to call the playVideo and pauseVideo methods in their respective play and pause handlers. The controls key in the playerVars object takes 0 and 1 as values. When set to 0, the player controls do not display in the player and vice versa.

Autoplay Video on Page Load

The API has an autoplay parameter which indicates whether the video should start playing automatically when the player loads or not. It takes the values 0 and 1 as well. When set to 1, it enables autoplay, allowing instant video playing without any human interaction with the player. While 0, which is the default value, disables autoplay. To enable autoplay, set the value of the autoplay key in the playerVars object to 1.

1<YouTube
2 videoId={youtubeID}
3 onReady={onReady}
4 opts={{
5 playerVars: {
6 autoplay: 1,
7 },
8 }}
9 />

Create a Video Playlist

The YouTube IFrame API also provides a playlist parameter that allows you to create a playlist of videos. To create a playlist, add a playlist object key to the playerVars object with a string value containing comma-separated IDs of the videos to form the playlist.

To demonstrate this in our application, update the opts props in the rendered YouTube component to look like this:

1<YouTube
2 videoId={youtubeID}
3 onReady={onReady}
4 opts={{
5 playerVars: {
6 playlist: '00AkMN9IAAY,4oCVDkb_EIs,7fPXI_MnBOY',
7 },
8 }}
9 />;

In the code, we used hardcoded video ids to define the playlist. Save the file to view the changes in the browser.

Manually Set the Start and Stop Time. The API accepts start and stop parameters that help determine when the player should start or stop playing the provided video in seconds relative to the start of the video.

1<YouTube
2 videoId={youtubeID}
3 onReady={onReady}
4 opts={
5 { playerVars: {
6 start: <<start-time>>,
7 stop: <<stop-time>>
8 } }
9 }
10 />

There are so many customization settings in the YouTube IFrame API that we can't possibly cover them all in this article. However, you can click here to learn more about other parameters to personalize your YouTube player.

You can find the complete project here on Github.

Conclusion

In this post, we saw how to embed a YouTube player into a React app using the react-youtube package. In addition, we looked at a few of the options for personalization.

Resources you may find helpful:

Ifeoma Imoh

Software Developer

Ifeoma is a software developer and technical content creator in love with all things JavaScript.