DEV Community

Hussain Ahmed Siddiqui
Hussain Ahmed Siddiqui

Posted on

🎧 Handle YouTube Audio Stream in React with Custom Controls

πŸ“Ί What is the YouTube IFrame API?

The YouTube IFrame Player API lets you embed and control YouTube videos using JavaScript. Unlike the standard embed method (which only gives you a video player), the IFrame API allows programmatic control over playback, volume, seeking, and more β€” directly in your web app.

πŸ” Key Features of the IFrame API

  • Embed YouTube videos dynamically using JavaScript
  • Control playback: play, pause, stop, seek
  • Listen to player events (e.g., ready, playing, paused, ended)
  • Customize player UI or hide it completely
  • Works cross-platform using an under the hood

βœ… 1. Setup

'use client';
import { useEffect, useRef, useState } from 'react'
Enter fullscreen mode Exit fullscreen mode
  • use client enables React client-side interactivity in Next.js App Router.
  • useState and useRef are used to manage state and player references.

βœ… 2. State and Refs

const playerRef = useRef<any>(null); // reference to YT player
const seekRef = useRef<any>(null);   // reference to the seek slider
const intervalRef = useRef<any>(null); // to store the interval for updating time

const [duration, setDuration] = useState(0);
const [currentTime, setCurrentTime] = useState(0);
const [volume, setVolume] = useState(50);
const [isPlaying, setIsPlaying] = useState(false);
const [isPlayerReady, setIsPlayerReady] = useState(false);
Enter fullscreen mode Exit fullscreen mode
  • duration, currentTime: Track progress.
  • volume: Stores volume level.
  • isPlaying: Toggles icon and playback.
  • isPlayerReady: Disables controls until API is ready.

βœ… 3. Load and Initialize YouTube Player API

useEffect(() => {
  const tag = document.createElement('script');
  tag.src = 'https://www.youtube.com/iframe_api';
  document.body.appendChild(tag);

  if (!window.YT) {
    window.onYouTubeIframeAPIReady = initPlayer;
  } else {
    initPlayer();
  }

  return () => {
    if (intervalRef.current) clearInterval(intervalRef.current);
  };
}, []);
Enter fullscreen mode Exit fullscreen mode
Dynamically loads the YouTube IFrame API.

Initializes player only once, and cleans up the interval on unmount.
Enter fullscreen mode Exit fullscreen mode

βœ… 4. Initialize Player

const initPlayer = () => {
  playerRef.current = new window.YT.Player('yt-player', {
    videoId: 'M7lc1UVf-VE',
    playerVars: { controls: 0, playsinline: 1 },
    events: {
      onReady: onPlayerReady,
      onStateChange: (e) => {
        setIsPlaying(e.data === window.YT.PlayerState.PLAYING);
      },
    },
  });
};
Enter fullscreen mode Exit fullscreen mode
  • Creates the YouTube player hidden in the DOM.
  • Disables default controls and tracks player state.

βœ… 5. Handle Player Ready Event

const onPlayerReady = () => {
  playerRef.current.setVolume(volume);
  setDuration(playerRef.current.getDuration());
  setIsPlayerReady(true);

  intervalRef.current = setInterval(() => {
    const current = playerRef.current.getCurrentTime();
    setCurrentTime(current);
    if (seekRef.current && duration > 0) {
      seekRef.current.value = ((current / duration) * 100).toString();
    }
  }, 1000);
};
Enter fullscreen mode Exit fullscreen mode
  • Sets default volume.
  • Gets video duration.
  • Updates seek bar every second using setInterval.

βœ… 6. Playback Toggle

const togglePlayback = () => {
  if (!isPlayerReady) return;
  if (isPlaying) {
    playerRef.current.pauseVideo();
  } else {
    playerRef.current.playVideo();
  }
};
Enter fullscreen mode Exit fullscreen mode
  • Plays or pauses audio based on the current isPlaying state.

βœ… 7. Format Time

const formatTime = (seconds: number) => {
  const min = Math.floor(seconds / 60);
  const sec = Math.floor(seconds % 60);
  return `${min}:${sec < 10 ? '0' + sec : sec}`;
};
Enter fullscreen mode Exit fullscreen mode
  • Converts seconds into mm:ss format for display under the seek bar.

βœ… 8. Render UI

<div className="bg-[#E7E7E7] p-4 rounded-xl w-full mx-auto shadow">
  <div id="yt-player" className="hidden" />
Enter fullscreen mode Exit fullscreen mode
  • Hidden YouTube player is injected here.
  • UI container is styled using Tailwind CSS.

βœ… 9. Controls: Play/Pause Button

<button
  onClick={togglePlayback}
  disabled={!isPlayerReady}
  className={`w-20 h-14 rounded-[32px] text-white text-2xl ${
    isPlayerReady ? 'bg-green-700 hover:bg-green-800' : 'bg-gray-400 cursor-not-allowed'
  }`}
>
  {isPlaying ? '⏸' : 'β–Ά'}
</button>
Enter fullscreen mode Exit fullscreen mode
  • Dynamically switches icon and color.
  • Disabled until the player is ready.

βœ… 10. Seek Bar & Timestamps

<input
  type="range"
  ref={seekRef}
  min="0"
  max="100"
  onChange={(e) => {
    const time = (parseFloat(e.target.value) / 100) * duration;
    playerRef.current.seekTo(time, true);
  }}
  disabled={!isPlayerReady}
  className="flex-grow h-4 rounded bg-gray-300 accent-[#001947]"
/>
Enter fullscreen mode Exit fullscreen mode
  • Displays and updates the progress.
  • Seekable after the player is ready.

βœ… 11. Volume Control

<input
  type="range"
  min="0"
  max="100"
  value={volume}
  disabled={!isPlayerReady}
  onChange={(e) => {
    const val = Number(e.target.value);
    setVolume(val);
    playerRef.current.setVolume(val);
  }}
  className="w-20 h-1 cursor-pointer accent-[#6D6D6D]"
/>
Enter fullscreen mode Exit fullscreen mode
  • Controls audio volume.
  • Synced with the internal YT player state.

Top comments (0)