Hook

useYtcnPlayer

Core hook. Returns player refs, reactive state, and imperative controls.

Signature

signature
const { containerRef, playerDivRef, state, controls } =
  useYtcnPlayer(options)

Options

PropTypeDefaultDescription
videoId*stringYouTube video ID to load.
startAtnumber0Initial playback position in seconds.
onEnd() => voidCalled when the video finishes playing.
onTimeUpdate(current: number, duration: number) => voidCalled every 250ms during playback with current time and total duration.
keyboardShortcutsbooleantrueEnable built-in keyboard shortcut handling.

Returns — State

The state object is reactive — it re-renders your component when any field changes.

PropTypeDefaultDescription
phase"thumbnail" | "loading" | "ready"Current player lifecycle phase. Thumbnail is shown first, then loading while iframe initializes, then ready when playback begins.
isPlayingbooleanWhether the video is currently playing.
isMutedbooleanWhether the video is currently muted.
volumenumberCurrent volume level from 0 to 100.
currentTimenumberCurrent playback position in seconds. Polled every 250ms.
durationnumberTotal video duration in seconds.
loadedFractionnumberBuffer progress from 0 to 1.
isLoadingbooleanTrue while the player is initializing.
isFullscreenbooleanTrue when in fullscreen mode.
playbackRate0.75 | 1 | 1.5 | 2Currently active playback speed.

Returns — Controls

PropTypeDefaultDescription
togglePlay()() => voidPlay or pause the video.
seekTo(seconds)(s: number) => voidJump to an absolute position in seconds.
seekRelative(delta)(d: number) => voidSeek ±N seconds from current position.
setVolume(0–100)(v: number) => voidSet volume level. Also unmutes if muted.
toggleMute()() => voidToggle mute state.
setSpeed(speed)(s: PlaybackSpeed) => voidSet playback rate to 0.75, 1, 1.5, or 2.
toggleFullscreen()() => voidEnter or exit fullscreen mode.

Stale closure warning

YouTube API callbacks fire outside React

YouTube API callbacks fire outside React's render cycle. Do not read state directly inside YT callbacks — read refs instead. useYtcnPlayer handles this internally. If you extend the hook, follow the same ref-sync pattern.
stale-closure.ts
// ❌ WRONG — state.volume is stale inside YT callback
onReady: () => player.setVolume(state.volume)

// ✅ CORRECT — ref is always current
onReady: () => player.setVolume(volumeRef.current)