Player

A media playback primitive split into composable parts.

Anatomy

Player is a headless context provider. It renders no DOM of its own. A source part (PlayerAudio or PlayerVideo) registers the media element, and control parts drive it.

TSX

Every part throws if rendered outside Player, so composition mistakes surface immediately.

For embedded sources like YouTube, Vimeo, SoundCloud, Twitch, HLS, and DASH, swap PlayerAudio for PlayerVideo and pass the URL. react-player v3 wraps each provider in a custom element that implements the HTMLMediaElement interface, so the same control parts (PlayerPlayPause, PlayerSeekButton, PlayerProgress, PlayerVolume, PlayerCurrentTime, PlayerDuration) drive them transparently — no special path. PlayerWaveform is the one exception, since it needs to fetch and decode the raw audio bytes; see its section below.

API

Player

The context provider. Owns the media element reference, subscribes to native media events (timeupdate, play, pause, durationchange, volumechange), and exposes everything through usePlayer. Polls currentTime with requestAnimationFrame while playing so word-level highlights stay smooth.

Prop

PlayerAudio

Headless native <audio>. No chrome, no controls. Drop it in once per Player to register the source.

Prop

PlayerVideo

A react-player wrapper for the cases where you do want a visible player surface. Handles video files, YouTube, Vimeo, and the rest of react-player's matrix. Pass controls={false} to render a chromeless surface and drive playback through the control parts.

Prop

PlayerPlayPause

A button that toggles playback. Swaps playIcon and pauseIcon based on the state. Sets data-playing on the element when running, so a single render element can style both states.

Prop

PlayerSeekButton

A button that skips by a fixed number of seconds. Negative values seek backward. Sets data-direction to forward or back.

Prop

PlayerCurrentTime

A live text label for the current playback position. Uses formatTimestamp by default, switching between m:ss and h:mm:ss based on the duration. Pass a format callback to swap in your own formatter.

Prop

PlayerDuration

A text label for the total length of the media. Reads 0:00 until metadata loads, then updates once the duration is known. Accepts the same format callback as PlayerCurrentTime.

Prop

PlayerProgress

A thin draggable seek bar with a hover thumb. Fills the available width.

Prop

PlayerTitle

A text slot for the media's name. An unopinionated <div> styled as small foreground text. Override the className to change the size, weight, or color.

Prop

PlayerMeta

A secondary text slot for things like the source, host, or recording length. Styled as muted, smaller text so it reads as a subtitle to PlayerTitle. Same <div> escape hatch through className.

Prop

PlayerMute

A button that toggles muted on the media element. Swaps muteIcon and unmuteIcon based on state and treats volume: 0 as muted too, so a slider pulled to zero shows the muted icon. Sets data-muted for one-render styling. When the slider is at zero, clicking the button restores the last non-zero volume (or 1 if it was always silent) instead of just flipping muted, so the user always gets audible feedback from a single click.

Prop

PlayerVolume

A small horizontal slider bound to the media element's volume. Reflects external volume changes (e.g. someone hitting the system volume keys with the audio focused) through the volumechange event.

Prop

PlayerWaveform

A clickable waveform. By default it decodes the audio file once via the Web Audio API, samples it into peaks, and draws bars with the played portion in the primary color. Peaks are cached by src so multiple waveforms or remounts don't re-decode.

Direct audio sources only. The decode runs fetch(src) + decodeAudioData, which only works for URLs that return raw audio bytes with permissive CORS. YouTube, Vimeo, Twitch, and similar embeds don't expose their media files at the URL, so the fetch returns HTML and the decode silently fails. For external embeds, use PlayerProgress instead. If you have the original audio stored separately, pre-compute peaks server-side and pass them via peaks — the waveform skips the decode entirely.

The decode is async, so bars render at minHeight while the file loads and snap into shape once peaks arrive. For instant render, pre-compute peaks and pass them via peaks. A one-off Node script using audio-decode writes the JSON next to the audio file:

Prop

usePlayer

Hook for reading playback state and driving the player from any child. Must be called inside <Player>.

State:

  • currentTime is the current playback position in seconds. Updated at animation-frame rate while playing.
  • duration is the total length of the media in seconds. Stays at 0 until metadata loads.
  • isPlaying is true while the media is playing, false when paused or ended.
  • volume is the current volume from 0 to 1.
  • muted is true when audio is silenced, either explicitly muted or with volume at 0.

Actions:

  • play() starts playback.
  • pause() pauses playback.
  • toggle() flips between play and pause.
  • jumpTo(seconds) seeks to an absolute time in seconds.
  • seekBy(delta) seeks relative to the current position. Negative values rewind.
  • setVolume(next) sets volume to a value between 0 and 1. Unmutes automatically when raised above zero.
  • toggleMute() flips the muted state. When called while volume is 0, it restores the last audible level so a single click always produces sound.
TSX

formatTimestamp

Helper that formats a seconds value as m:ss or h:mm:ss depending on a duration hint. Exposed so custom formatters can mirror the default before adding their own tweaks.