Build a Simple YouTube App with Vanilla JavaScript

The Tuts+ YouTube channel is fast approaching 1.5M subscribers. Let’s celebrate this great achievement by creating something YouTube-oriented! We’re going to build a simple, yet fully functional YouTube app with Vanilla JavaScript.

The concept will be pretty straightforward; we’ll build a simple UI where we can enter the ID of a channel and our app will return info about it.

1. Scaffolding the YouTube App

Before we start creating our app, there are a few things that we have to address.

Grab a YouTube API key

As a first and mandatory thing, we should get a YouTube API key that will give us access to the YouTube Data API. To do so, we should follow the instructions on this page and set up a project in the Google Cloud Console with the YouTube Data API v3 enabled. In my case, I’ve already done it while building the app. Now, it’s your turn to generate an API and include it in your forked demo.

this is the api you needthis is the api you needthis is the api you need

For production environments, remember that it’s always wise to restrict the API requests to specific websites, IP addresses, etc.

Grab a Lottie Animation

Optionally, to make our app as unique as possible, we’ll grab a Lottie animation from the LottieFiles library and play it for channels with 1M or more subscribers. 

Confetti Lottie animationConfetti Lottie animationConfetti Lottie animation

First, we’ll generate an asset link for this animation and customize it as we wish.

Generating an asset link for this animationGenerating an asset link for this animationGenerating an asset link for this animation

As things move quickly, at this point, the LottieFiles team suggests using the dotLottie file format instead of the traditional Lottie JSON to reduce the file size.

Selecting the dotLottie file format for the animationSelecting the dotLottie file format for the animationSelecting the dotLottie file format for the animation

In our case, as we’re using a CodePen demo, we’ll import the required .mjs file like this:

1
import { DotLottiePlayer } from "https://webdesign.tutsplus.com/https://unpkg.com/@dotlottie/player-component@latest/dist/dotlottie-player.mjs"https://webdesign.tutsplus.com/;

Then, as we’ll see in an upcoming section, we’ll include the generated dotlottie-player component in the markup that represents the channel info.

Besides, if you need a refresher about how to include Adobe After Effects animations on a web page and Lottie Animations in general, consider the following tutorials:

2. Define the Page Markup

Let’s now focus on the app development.

We’ll only define a section that will include a heading, a search form, and an empty span element.

We’ll set the input element as required and force it to wait 24 characters to avoid unnecessary AJAX requests. From my tests, I’ve seen that the length of a YouTube channel ID is 24, although you can update the minlength and maxlength attribute values if you notice something different.

The span element will appear with an appropriate message under certain conditions. For example, if we search for a YouTube channel ID that doesn’t exist or if, for some reason, the response is unsuccessful (i.e. 400 Bad Request, 404 Not Found, etc.). 

Along the way, we’ll see the markup for the channel info that will be generated dynamically. 

Here’s the initial page markup:

1
<section class="top-banner"https://webdesign.tutsplus.com/>
2
  <div class="container"https://webdesign.tutsplus.com/>
3
    <div class="text"https://webdesign.tutsplus.com/>
4
      <h1>Simple App With the YouTube API</h1>
5
      <p class="label"https://webdesign.tutsplus.com/>Use <mark>UC8lxnUR_CzruT2KA6cb7p0Q</mark> for testing which refers to the Envato Tuts+ channel ID</p>
6
    </div>
7
    <form>
8
      <input type="search" minlength="24" maxlength="24" placeholder="Insert a valid YT channel ID" autofocus required>
9
      <button type="submit"https://webdesign.tutsplus.com/>SUBMIT</button>
10
      <span class="msg"https://webdesign.tutsplus.com/></span>
11
    </form>
12
  </div>
13
</section>

The autofocus attribute of the search field won’t work unless you view the CodePen demo in debug mode.

Find a YouTube Channel ID

One quick way to find the ID of a YouTube channel is through the page source. First, navigate to the desired channel page, then view its source code and search for https://www.youtube.com/channel/. The channel ID will come after this base URL.  

Envato Tuts+ Channel IDEnvato Tuts+ Channel IDEnvato Tuts+ Channel ID
Envato Tuts+ Channel ID

Traversy Media Channel IDTraversy Media Channel IDTraversy Media Channel ID
Traversy Media Channel ID

3. Set the Main Styles

As this is a large tutorial, for the sake of simplicity, we’ll skip the starting styles and only concentrate on the main ones—you can view all of them by clicking the CSS tab of the demo.

Form Styles

On medium screens and above (>700px), the layout should look like this:

The form layout on large screensThe form layout on large screensThe form layout on large screens

On smaller screens, the form elements will split into two lines:

The form layout on mobile screensThe form layout on mobile screensThe form layout on mobile screens

Here are the associated styles:

1
/*CUSTOM VARIABLES HERE*/
2

3
.top-banner form {
4
  position: relative;
5
  display: grid;
6
  grid-template-columns: 1fr auto;
7
  grid-gap: 15px;
8
  align-items: center;
9
  justify-content: center;
10
  max-width: 1000px;
11
}
12

13
.top-banner form input {
14
  font-size: clamp(24px, 2vw, 32px);
15
  height: 40px;
16
  padding-bottom: 10px;
17
  border-bottom: 1px solid currentColor;
18
}
19

20
.top-banner form input::placeholder {
21
  opacity: 1;
22
  color: var(--white);
23
}
24

25
.top-banner form button {
26
  font-weight: bold;
27
  padding: 15px 30px;
28
  border-radius: 5px;
29
  background: var(--red);
30
  transition: background 0.3s ease-in-out;
31
}
32

33
.top-banner form button:hover {
34
  background: var(--darkred);
35
}
36

37
.top-banner form .msg {
38
  position: absolute;
39
  top: 100%;
40
  left: 0;
41
}
42

43
@media (max-width: 700px) {
44
  .top-banner form {
45
    grid-template-columns: 1fr;
46
  }
47

48
  .top-banner form .msg {
49
    position: static;
50
  }
51
}

Channel Styles

As soon as we successfully get back from the server info for a channel, they will appear in a card layout like this:

The channel infoThe channel infoThe channel info

The styles aren’t anything too complicated, so we won’t go into more detail at this point:

1
/*CUSTOM VARIABLES HERE*/
2

3
.card {
4
  padding: 4%;
5
  text-align: center;
6
  margin-top: 70px;
7
  color: var(--white);
8
  background: var(--total-black);
9
  border-radius: 7px;
10
  overflow: hidden;
11
}
12

13
.card .details img {
14
  border-radius: 50%;
15
}
16

17
.card .details .title {
18
  margin-top: 10px;
19
}
20

21
.card .details .description {
22
  max-width: 80%;
23
  margin: 30px auto 0;
24
}
25

26
.card .total-videos {
27
  position: relative;
28
  z-index: 1;
29
  margin-top: 30px;
30
}
31

32
.card .total-subscribers {
33
  position: relative;
34
  display: inline-grid;
35
  grid-template-columns: auto auto;
36
  grid-gap: 10px;
37
  align-items: center;
38
  font-weight: bold;
39
  margin-top: 60px;
40
  background: var(--red);
41
}
42

43
.card .total-subscribers dotlottie-player {
44
  position: absolute;
45
  top: 50%;
46
  left: 50%;
47
  transform: translate(-50%, -50%);
48
}
49

50
.card .total-subscribers .outer {
51
  padding: 10px;
52
}
53

54
.card .total-subscribers svg {
55
  fill: var(--red);
56
  background: var(--white);
57
  padding: 5px;
58
  box-sizing: content-box;
59
}

4. Add the JavaScript

At this moment, we’re ready to build the core functionality of our YouTube app. Let’s do it!

On Form Submission

Each time a user submits the form by pressing the Enter key or the Submit button, we’ll do two things:

  1. Stop the form from submitting, hence prevent reloading the page.
  2. Grab the value that is contained in the search field.

Here’s the starting code:

1
const topBanner = document.querySelector("https://webdesign.tutsplus.com/.top-banner"https://webdesign.tutsplus.com/);
2
const form = topBanner.querySelector("https://webdesign.tutsplus.com/form"https://webdesign.tutsplus.com/);
3
const input = topBanner.querySelector("https://webdesign.tutsplus.com/input"https://webdesign.tutsplus.com/);
4

5
form.addEventListener("https://webdesign.tutsplus.com/submit"https://webdesign.tutsplus.com/, (e) => {
6
  e.preventDefault();
7
  const channelId = input.value;
8
});

Perform an AJAX Request

Before we go through the AJAX request, it’s important to read the docs and understand how to structure it. In our case, we want to get channel info, so we’ll focus on the channel endpoint and pass the following parameters:

  1. The part parameter with values the snippet and statistics names. 
  2. The API key. Again, you should use your own key.
  3. The channel ID we’re interested to get info. In a previous section, we covered a way to find the ID of an existing channel.

We can even experiment with the HTTP requests through the helper tool that is available on the right side of this page

Experiment with a test requestExperiment with a test requestExperiment with a test request

With all the above in mind, our request URL should look something like this:

1
const BASE_URL ="https://webdesign.tutsplus.com/https://www.googleapis.com/youtube/v3/channels?part=statistics,snippet"https://webdesign.tutsplus.com/;
2
const API_KEY = "https://webdesign.tutsplus.com/AIzaSyAHupLf37J-vEziyQ-pItfoaLS5XUqdVq8"https://webdesign.tutsplus.com/;
3
const channelID = input.value;
4

5
const url = `${BASE_URL}&id=${channelId}&key=${API_KEY}`;

We’ll use the Fetch API to perform the AJAX request—I assume you’re familiar with this technique. There’s also a series if you need a refresher. As discussed previously, we’ll add proper error handling for unsuccessful cases. For example, if we search for a non-existing channel or the status request hasn’t succeeded.

An example of error handlingAn example of error handlingAn example of error handling

So, our AJAX request would look something like this:

1
...
2

3
form.addEventListener("https://webdesign.tutsplus.com/submit"https://webdesign.tutsplus.com/, (e) => {
4
  ...
5

6
  fetchYTStatistics(channelId)
7
    .then((data) => {
8
      if (typeof data.items !== "https://webdesign.tutsplus.com/undefined"https://webdesign.tutsplus.com/) {
9
        createCard(data);
10
      } else {
11
        msg.textContent = "https://webdesign.tutsplus.com/Please search for a valid YT channel ID 😩"https://webdesign.tutsplus.com/;
12
      }
13
    })
14
    .catch((error) => {
15
      msg.textContent = error;
16
    });
17
});
18
    
19
async function fetchYTStatistics(channelId) {
20
  const url = `${BASE_URL}&id=${channelId}&key=${API_KEY}`;
21
  const response = await fetch(url);
22

23
  if (!response.ok) {
24
    return Promise.reject(
25
      `Something isn't working as expected. Error: ${response.status}`
26
    );
27
  }
28
  const data = await response.json();
29
  return data;
30
}

Here’s an example of the response data:

An example of the response dataAn example of the response dataAn example of the response data

Build the Card

With the AJAX request in place, each time we type a channel ID in the search field, the API will return channel data if they are available. We’ll then collect only the required data and attach it to the page as a card component.

The channel infoThe channel infoThe channel info

Two things to note here:

  • The Lottie animation will play only if the channel subscribers are at least 1M.
  • We’ll use the NumberFormat API to format the numbers related to the channel videos and subscribers. 
  • The external links won’t work unless you view the CodePen demo in debug mode.

Here’s the code responsible for this job:

1
function createCard(data) {
2
  const allData = data.items[0];
3
  const { customUrl, title, description, thumbnails } = allData.snippet;
4
  const { default: thumbnail } = thumbnails;
5
  const { videoCount, subscriberCount } = allData.statistics;
6
  const div = document.createElement("https://webdesign.tutsplus.com/div"https://webdesign.tutsplus.com/);
7
  div.classList.add("https://webdesign.tutsplus.com/card"https://webdesign.tutsplus.com/, "https://webdesign.tutsplus.com/container"https://webdesign.tutsplus.com/);
8

9
  const markup = `
10
    <div class="details">
11
      <img width="https://webdesign.tutsplus.com/${thumbnail.width}" height="https://webdesign.tutsplus.com/${thumbnail.height}" src="https://webdesign.tutsplus.com/${thumbnail.url}" alt="https://webdesign.tutsplus.com/${title}">
12
      <div class="title">
13
        <a href="https://www.youtube.com/${customUrl}" target="_blank">${title}</a>
14
      </div>
15
      <p class="description">${description}</p>
16
    </div>
17
    <div class="total-videos">
18
      <a href="https://www.youtube.com/${customUrl}/videos" target="_blank">Browse</a>
19
      <span class="count">${formatNumber(videoCount)}</span> videos
20
    </div>
21
    <div class="total-subscribers">
22
      ${
23
        subscriberCount >= 1000000
24
          ? `<dotlottie-player src="https://lottie.host/5fa38a1c-c8ba-4c3d-83b5-1a99b8796da3/jJFC2WMsxa.lottie" background="transparent" speed="1" style="width: 300px; height: 300px;" loop autoplay></dotlottie-player>`
25
          : ""
26
      }
27
      <span class="outer">
28
        <span class="count">${formatNumber(subscriberCount)}</span>
29
        Subscribers
30
      </span>
31
      <svg xmlns="https://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 24 24">
32
        <path d="M4.652 0h1.44l.988 3.702.916-3.702h1.454l-1.665 5.505v3.757h-1.431v-3.757l-1.702-5.505zm6.594 2.373c-1.119 0-1.861.74-1.861 1.835v3.349c0 1.204.629 1.831 1.861 1.831 1.022 0 1.826-.683 1.826-1.831v-3.349c0-1.069-.797-1.835-1.826-1.835zm.531 5.127c0 .372-.19.646-.532.646-.351 0-.554-.287-.554-.646v-3.179c0-.374.172-.651.529-.651.39 0 .557.269.557.651v3.179zm4.729-5.07v5.186c-.155.194-.5.512-.747.512-.271 0-.338-.186-.338-.46v-5.238h-1.27v5.71c0 .675.206 1.22.887 1.22.384 0 .918-.2 1.468-.853v.754h1.27v-6.831h-1.27zm2.203 13.858c-.448 0-.541.315-.541.763v.659h1.069v-.66c.001-.44-.092-.762-.528-.762zm-4.703.04c-.084.043-.167.109-.25.198v4.055c.099.106.194.182.287.229.197.1.485.107.619-.067.07-.092.105-.241.105-.449v-3.359c0-.22-.043-.386-.129-.5-.147-.193-.42-.214-.632-.107zm4.827-5.195c-2.604-.177-11.066-.177-13.666 0-2.814.192-3.146 1.892-3.167 6.367.021 4.467.35 6.175 3.167 6.367 2.6.177 11.062.177 13.666 0 2.814-.192 3.146-1.893 3.167-6.367-.021-4.467-.35-6.175-3.167-6.367zm-12.324 10.686h-1.363v-7.54h-1.41v-1.28h4.182v1.28h-1.41v7.54zm4.846 0h-1.21v-.718c-.223.265-.455.467-.696.605-.652.374-1.547.365-1.547-.955v-5.438h1.209v4.988c0 .262.063.438.322.438.236 0 .564-.303.711-.487v-4.939h1.21v6.506zm4.657-1.348c0 .805-.301 1.431-1.106 1.431-.443 0-.812-.162-1.149-.583v.5h-1.221v-8.82h1.221v2.84c.273-.333.644-.608 1.076-.608.886 0 1.18.749 1.18 1.631v3.609zm4.471-1.752h-2.314v1.228c0 .488.042.91.528.91.511 0 .541-.344.541-.91v-.452h1.245v.489c0 1.253-.538 2.013-1.813 2.013-1.155 0-1.746-.842-1.746-2.013v-2.921c0-1.129.746-1.914 1.837-1.914 1.161 0 1.721.738 1.721 1.914v1.656z" />
33
      </svg>
34
    </div>
35
  `;
36
    
37
  div.innerHTML = markup;
38
  document.body.appendChild(div);
39
}
40

41
function formatNumber(number) {
42
  return new Intl.NumberFormat("https://webdesign.tutsplus.com/en"https://webdesign.tutsplus.com/, {
43
    notation: "https://webdesign.tutsplus.com/compact"
44
  }).format(number);
45
}

The generated markup as it’s rendered in the browser console:

The card markupThe card markupThe card markup

Reset Things

Lastly, after the AJAX request, we’ll do the following:

  • Remove the .card element from the page.
  • Clear the content of the .msg element.
  • Clear the value of the search field and give focus to that field.

Here’s the related code:

1
...
2

3
if (document.querySelector("https://webdesign.tutsplus.com/.card"https://webdesign.tutsplus.com/)) {
4
  document.querySelector("https://webdesign.tutsplus.com/.card"https://webdesign.tutsplus.com/).remove();
5
}
6
msg.textContent = ""https://webdesign.tutsplus.com/;
7
form.reset();
8
input.focus();

Your YouTube App Is Ready!

Done, folks! This really was quite a long journey, so thanks for following along! I hope you enjoyed the end result and that helped you learn some new things.

Once again, don’t forget to put your own key for live app testing!

As a reminder, let’s look again at the app:

As always, thanks a lot for reading!

Next Steps

There are so many things that you can do to extend the functionality of this YouTube app. Here are some thoughts:

  • Create another section to show the latest channel videos.
  • Instead of showing just a channel each time, modify the code to show multiple channel info simultaneously in a grid format, as we did with the weather app.

If there’s anything else that you might want to see as an app extension, let us know on X or in the demo comments!

Discover More JavaScript Tutorials and Resources

Interested in practicing modern JavaScript through fun hands-on projects? If so, check out these JavaScript tutorials: