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)