Migration from 1.x

This guide helps you migrate from react-native-youtube-bridge 1.x to 2.x.

The 2.x API was redesigned to be more React-friendly by using hooks, similar to modern Expo APIs such as expo-audio and expo-video.

Overview of changes

Aspect1.x2.x
API styleImperative, ref-basedDeclarative, hook-based
Main render APIYoutubePlayer componentuseYouTubePlayer + YoutubeView
Event handlingCallback propsuseYouTubeEvent hook
State managementManual useStateReactive event subscriptions
Player controlplayerRef.current.method()player.method()
Initial configComponent props / playerVarsHook config

1. Replace the component API

Before: 1.x

import { YoutubePlayer } from 'react-native-youtube-bridge';

<YoutubePlayer
  ref={playerRef}
  source="AbZH7XWDW_k"
  height={400}
  playerVars={{
    autoplay: true,
    controls: true,
    playsinline: true,
    rel: false,
    muted: true,
  }}
  onReady={handleReady}
  onStateChange={handleStateChange}
  onProgress={handleProgress}
  onError={handleError}
/>;

After: 2.x

import { YoutubeView, useYouTubePlayer } from 'react-native-youtube-bridge';

const player = useYouTubePlayer('AbZH7XWDW_k', {
  autoplay: true,
  controls: true,
  playsinline: true,
  rel: false,
  muted: true,
});

<YoutubeView player={player} height={400} />;

2. Move player config to useYouTubePlayer

In 1.x, most player options lived on the component through playerVars.

<YoutubePlayer
  source="AbZH7XWDW_k"
  playerVars={{
    autoplay: true,
    controls: true,
    muted: true,
  }}
/>

In 2.x, pass those options as the second argument to useYouTubePlayer.

const player = useYouTubePlayer('AbZH7XWDW_k', {
  autoplay: true,
  controls: true,
  muted: true,
});

<YoutubeView player={player} />;

Rendering props such as height, width, style, iframeStyle, webViewStyle, webViewProps, useInlineHtml, and webViewUrl stay on YoutubeView.

3. Migrate events to useYouTubeEvent

useYouTubeEvent supports both state-style subscriptions and callback-style side effects.

Before: 1.x callback props

const [isPlaying, setIsPlaying] = useState(false);
const [currentTime, setCurrentTime] = useState(0);
const [duration, setDuration] = useState(0);
const [playbackRate, setPlaybackRate] = useState(1);
const [availableRates, setAvailableRates] = useState([1]);

const handleReady = useCallback((playerInfo) => {
  if (playerInfo?.availablePlaybackRates) {
    setAvailableRates(playerInfo.availablePlaybackRates);
  }
}, []);

const handleStateChange = useCallback((state) => {
  setIsPlaying(state === PlayerState.PLAYING);
}, []);

const handleProgress = useCallback((progress) => {
  setCurrentTime(progress.currentTime);
  setDuration(progress.duration);
}, []);

const handlePlaybackRateChange = useCallback((rate) => {
  setPlaybackRate(rate);
}, []);

<YoutubePlayer
  source="AbZH7XWDW_k"
  onReady={handleReady}
  onStateChange={handleStateChange}
  onProgress={handleProgress}
  onPlaybackRateChange={handlePlaybackRateChange}
/>;

After: 2.x event hook

const player = useYouTubePlayer('AbZH7XWDW_k');

// State-style subscriptions
const playbackRate = useYouTubeEvent(player, 'playbackRateChange', 1);
const progress = useYouTubeEvent(player, 'progress', 1000);
const state = useYouTubeEvent(player, 'stateChange');

const currentTime = progress?.currentTime ?? 0;
const duration = progress?.duration ?? 0;
const isPlaying = state === PlayerState.PLAYING;

// Callback-style side effects
const [availableRates, setAvailableRates] = useState([1]);

useYouTubeEvent(player, 'ready', (playerInfo) => {
  if (playerInfo.availablePlaybackRates) {
    setAvailableRates(playerInfo.availablePlaybackRates);
  }
});

useYouTubeEvent(player, 'autoplayBlocked', () => {
  console.log('Autoplay was blocked');
});

useYouTubeEvent(player, 'error', (error) => {
  console.error('Player error:', error);
});

return <YoutubeView player={player} />;

4. Replace ref-based controls

Before: 1.x ref methods

const playerRef = useRef<PlayerControls>(null);

const play = () => playerRef.current?.play();
const pause = () => playerRef.current?.pause();
const stop = () => playerRef.current?.stop();
const seekTo = (time: number) => playerRef.current?.seekTo(time, true);
const setVolume = (volume: number) => playerRef.current?.setVolume(volume);
const mute = () => playerRef.current?.mute();
const unMute = () => playerRef.current?.unMute();

const getPlayerInfo = async () => {
  const currentTime = await playerRef.current?.getCurrentTime();
  const duration = await playerRef.current?.getDuration();
  const state = await playerRef.current?.getPlayerState();
};

<YoutubePlayer ref={playerRef} source="AbZH7XWDW_k" />;

After: 2.x direct player methods

const player = useYouTubePlayer('AbZH7XWDW_k');

const play = () => player.play();
const pause = () => player.pause();
const stop = () => player.stop();
const seekTo = (time: number) => player.seekTo(time, true);
const setVolume = (volume: number) => player.setVolume(volume);
const mute = () => player.mute();
const unMute = () => player.unMute();

const getPlayerInfo = async () => {
  const currentTime = await player.getCurrentTime();
  const duration = await player.getDuration();
  const state = await player.getPlayerState();
};

<YoutubeView player={player} />;

Async getter methods should be called after the ready event when you need reliable values. Before YoutubeView attaches the underlying controller, a getter can return undefined.

5. Update source handling

Both versions support raw video IDs and YouTube URLs, but 2.x passes source to useYouTubePlayer instead of YoutubePlayer.

const playerA = useYouTubePlayer('AbZH7XWDW_k');
const playerB = useYouTubePlayer({ videoId: 'AbZH7XWDW_k' });
const playerC = useYouTubePlayer({ url: 'https://youtube.com/watch?v=AbZH7XWDW_k' });

6. Rendering mode migration

The rendering-mode props still belong to the render surface, so move them to YoutubeView.

Before: 1.x

<YoutubePlayer
  source="AbZH7XWDW_k"
  useInlineHtml={false}
  webViewUrl="https://your-custom-player.com"
/>

After: 2.x

const player = useYouTubePlayer('AbZH7XWDW_k');

<YoutubeView player={player} useInlineHtml={false} webViewUrl="https://your-custom-player.com" />;

Migration checklist

Required changes

  • Replace YoutubePlayer imports with YoutubeView and useYouTubePlayer
  • Move source from the component to useYouTubePlayer(source)
  • Move playerVars into the useYouTubePlayer(source, config) second argument
  • Replace event handler props with useYouTubeEvent hooks
  • Remove useRef<PlayerControls> for normal playback control
  • Replace playerRef.current?.method() with player.method()
  • Keep render-only props such as height, width, styles, and WebView options on YoutubeView

Optional improvements

  • Replace manual useState mirrors with state-style useYouTubeEvent subscriptions
  • Add callback-style useYouTubeEvent(player, 'error', ...) error handling
  • Use muteChange if your UI needs reactive muted state
  • Remove now-unnecessary callback memoization used only for 1.x prop stability

Breaking changes summary

  • YoutubePlayer was replaced by useYouTubePlayer + YoutubeView
  • Event props were removed in favor of useYouTubeEvent
  • Ref-based control moved to direct player methods
  • playerVars moved from component props to hook config
  • Manual state management can often be replaced with reactive event values

Most apps can migrate one screen at a time: create the player with useYouTubePlayer, render it with YoutubeView, then move events and ref calls into hooks/direct player methods.