# Video.js v10 — React API (complete)
> Every React API page in one file (about 52k tokens). Index with descriptions: https://videojs.org/docs/framework/react/reference/api/llms.txt
---
# createPlayer
Factory function that creates a typed Player component and hooks
## Import
```tsx
import { createPlayer } from "@videojs/react";
import { videoFeatures } from "@videojs/react/video";
```
`createPlayer` is the entry point for setting up a Video.js player in React. It accepts a configuration object with a `features` array and returns hooks and components for building a player.
The hook is typed according to the provided features, giving you full type safety for state selectors and actions.
```tsx
import { Container, createPlayer } from '@videojs/react';
import { videoFeatures } from '@videojs/react/video';
const { Player, usePlayer, useMedia } = createPlayer({
features: videoFeatures,
});
// Container is imported independently from createPlayer.
```
## Examples
### Basic Usage
**App.tsx**
```tsx
import { Container, createPlayer } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
const { Player, usePlayer } = createPlayer({
features: videoFeatures,
});
function Controls() {
const store = usePlayer();
const paused = usePlayer((s) => s.paused);
return (
(paused ? store.play() : store.pause())}>
{paused ? 'Play' : 'Pause'}
);
}
export default function BasicUsage() {
return (
);
}
```
**App.css**
```css
.media-container {
position: relative;
}
.media-container video {
width: 100%;
}
.controls {
position: absolute;
bottom: 10px;
left: 10px;
}
.button {
padding-block: 8px;
padding-inline: 20px;
color: black;
cursor: pointer;
background: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 9999px;
backdrop-filter: blur(10px);
}
```
## API Reference
### Video
`createPlayer(config): CreatePlayerResult`
Create a player instance with a typed Player component and hooks.
#### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `config` (required) | `{ features: [PlayerFeature, PlayerFeature, PlayerFeature, PlayerFeature, PlayerFeature, PlayerFeature, PlayerFeature, PlayerFeature, PlayerFeature, PlayerFeature, PlayerFeature, PlayerFeature, PlayerFeature, PlayerFeature, typeof metadataFeature]; displayName?: string }` | — | Player configuration with features and optional display name. |
#### Return Value
| Property | Type | Description |
| --- | --- | --- |
| `Player` | `FC>>` | Provides a new player store to its descendants without adding a layout element. |
| `usePlayer` | `{ (): VideoPlayerStore; (selector: ((state: VideoPlayerStore['state']) => R)): R }` | Accesses the configured store, or subscribes to a selected value from it. |
| `useMedia` | `(() => Media \| null)` | Returns the media currently attached beneath the generated Player, or `null` before attachment. |
### Audio
`createPlayer(config): CreatePlayerResult`
Create a player for audio media.
#### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `config` (required) | `{ features: [PlayerFeature, PlayerFeature, PlayerFeature, PlayerFeature, PlayerFeature, PlayerFeature, PlayerFeature, typeof metadataFeature]; displayName?: string }` | — | Player configuration with features and optional display name. |
#### Return Value
| Property | Type | Description |
| --- | --- | --- |
| `Player` | `FC>>` | Provides a new player store to its descendants without adding a layout element. |
| `usePlayer` | `{ (): AudioPlayerStore; (selector: ((state: AudioPlayerStore['state']) => R)): R }` | Accesses the configured store, or subscribes to a selected value from it. |
| `useMedia` | `(() => Media \| null)` | Returns the media currently attached beneath the generated Player, or `null` before attachment. |
### Generic
`createPlayer(config): CreatePlayerResult>`
Create a player with custom features.
#### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `config` (required) | `{ features: Features; displayName?: string }` | — | Player configuration with features and optional display name. |
#### Return Value
| Property | Type | Description |
| --- | --- | --- |
| `Player` | `FC>>>` | Provides a new player store to its descendants without adding a layout element. |
| `usePlayer` | `{ (): PlayerStore; (selector: ((state: PlayerStore['state']) => R)): R }` | Accesses the configured store, or subscribes to a selected value from it. |
| `useMedia` | `(() => Media \| null)` | Returns the media currently attached beneath the generated Player, or `null` before attachment. |
---
# usePlayer
Hook to access the player store from within a Player
## Import
```tsx
import { usePlayer } from "@videojs/react";
```
The `usePlayer` hook returned by [`createPlayer`](https://videojs.org/docs/framework/react/reference/api/create-player) gives you access to the player store from any component within its `Player`. It’s typed to the features you passed to `createPlayer`, and has two overloads — without arguments for direct store access, or with a selector for reactive state subscriptions.
```tsx
const { Player, usePlayer } = createPlayer({ features: videoFeatures });
```
The pair works exactly like React Context: [`Player`](https://videojs.org/docs/framework/react/reference/components/player) is the Provider, and `usePlayer` is its `useContext`-style consumer, so the calling component must render inside `Player`. You can still read state anywhere: `Player` renders no DOM of its own, so lift it above every component that needs player state, the way you’d lift any Provider.
`usePlayer` is also available as a standalone import (`import { usePlayer } from '@videojs/react'`), but the standalone version returns an untyped `UnknownStore`. Pass a premade selector to recover typing:
```tsx
import { usePlayer, selectPlayback } from '@videojs/react';
usePlayer(); // UnknownStore (state: unknown)
usePlayer(selectPlayback); // MediaPlaybackState | undefined
```
## Examples
### Store Access
Call `usePlayer()` without arguments to get the store instance. Use this for imperative actions like play, pause, and volume changes. The component does not re-render on state changes.
**App.tsx**
```tsx
import { Container, createPlayer } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
const { Player, usePlayer } = createPlayer({
features: videoFeatures,
});
function Controls() {
const store = usePlayer();
return (
store.play()}>
Play
store.pause()}>
Pause
);
}
export default function StoreAccess() {
return (
);
}
```
**App.css**
```css
.media-container {
position: relative;
}
.media-container video {
width: 100%;
}
.controls {
display: flex;
gap: 6px;
padding: 12px;
background: rgba(0, 0, 0, 0.05);
border-top: 1px solid rgba(0, 0, 0, 0.1);
}
.controls button {
padding: 4px 12px;
font-size: 0.8125rem;
color: #111827;
cursor: pointer;
background: white;
border: 1px solid #ccc;
border-radius: 6px;
}
```
### Selector Subscription
Pass a selector function to subscribe to specific state. The component re-renders when the selected value changes, using shallow equality by default.
**App.tsx**
```tsx
import { Container, createPlayer } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
const { Player, usePlayer } = createPlayer({
features: videoFeatures,
});
function StateDisplay() {
const state = usePlayer((s) => ({
paused: s.paused,
currentTime: s.currentTime,
duration: s.duration,
}));
return (
Paused
{String(state.paused)}
Time
{state.currentTime.toFixed(1)}s / {state.duration.toFixed(1)}s
);
}
export default function Selector() {
return (
);
}
```
**App.css**
```css
.media-container {
position: relative;
}
.media-container video {
width: 100%;
}
.panel {
display: flex;
gap: 16px;
padding: 12px;
margin: 0;
font-size: 0.8125rem;
background: rgba(0, 0, 0, 0.05);
border-top: 1px solid rgba(0, 0, 0, 0.1);
}
.panel div {
display: flex;
gap: 8px;
}
.panel dt {
color: #6b7280;
}
.panel dd {
margin: 0;
font-variant-numeric: tabular-nums;
}
```
## API Reference
### Without Selector
`usePlayer(): UnknownStore`
Access the player store from within a Player.
This standalone hook has no knowledge of your configured features, so it returns an untyped `UnknownStore` whose state properties are typed as `unknown`. For typed access, use the `usePlayer` returned by `createPlayer()`, or pass a premade selector to recover the type from its return value.
#### Return Value
| Property | Type |
| --- | --- |
| `$state` | `{ current: Readonly; subscribe(callback: (() => void), options?: SubscribeOptions): (() => void) }` |
| `target` | `unknown \| null` |
| `destroyed` | `boolean` |
| `state` | `Record` |
| `attach` | `((target: unknown) => (() => void))` |
| `destroy` | `(() => void)` |
| `subscribe` | `((callback: StateChange, options?: SubscribeOptions) => (() => void))` |
### With Selector
`usePlayer(selector): R`
Select a value from the player store. Re-renders when the selected value changes.
The selector receives `UnknownState`, so an inline selector returns `unknown`. Pass a premade selector (e.g. `selectPlayback`) to get a typed result.
#### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `selector` (required) | `((state: UnknownState) => R)` | — | Derives a value from the player store state. |
#### Return Value
`R`
---
# useOptionalPlayer
Hook to access or select Player state when a Player is available
## Import
```tsx
import { useOptionalPlayer } from "@videojs/react";
```
## Usage
Without a selector, the hook returns the player store or `undefined` outside `Player`.
```tsx
const store = useOptionalPlayer();
```
With a selector, it subscribes while a player is available and returns the selected value. The selector is not called outside `Player`.
```tsx
import { selectPlayback, useOptionalPlayer } from "@videojs/react";
const playback = useOptionalPlayer(selectPlayback);
```
Use `usePlayer` when the component requires a surrounding player.
## API Reference
### Without Selector
`useOptionalPlayer(): UnknownStore | undefined`
Returns the player store when available, or `undefined` outside a Player.
#### Return Value
| Property | Type |
| --- | --- |
| `$state` | `{ current: Readonly; subscribe(callback: (() => void), options?: SubscribeOptions): (() => void) }` |
| `target` | `unknown \| null` |
| `destroyed` | `boolean` |
| `state` | `Record` |
| `attach` | `((target: unknown) => (() => void))` |
| `destroy` | `(() => void)` |
| `subscribe` | `((callback: StateChange, options?: SubscribeOptions) => (() => void))` |
### With Selector
`useOptionalPlayer(selector): R | undefined`
Selects a player value when available, or returns `undefined` outside a Player.
#### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `selector` (required) | `((state: UnknownState) => R)` | — | Derives a value from the player store state. |
#### Return Value
`R | undefined`
---
# useMedia
Hook to access the media attached to the nearest Player
`useMedia` returns the media attached to the nearest `Player`, or `null` until a media component mounts inside it.
## Import
```tsx
import { useMedia } from "@videojs/react";
```
[`createPlayer`](https://videojs.org/docs/framework/react/reference/api/create-player) also returns `useMedia` alongside `Player` and `usePlayer`:
```tsx
const { Player, usePlayer, useMedia } = createPlayer({ features: videoFeatures });
```
Both forms are the same hook and return the same `Media | null` type. Unlike [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player), the `createPlayer` form doesn’t add typing from your features.
## Usage
Call `useMedia` from a component rendered inside `Player`. Calling it outside `Player` throws, because it reads the nearest `Player`’s context.
**PlayFromStart.tsx**
```tsx
import { isMediaSeekCapable, useMedia } from "@videojs/react";
export function PlayFromStart() {
const media = useMedia();
function playFromStart() {
if (isMediaSeekCapable(media)) media.currentTime = 0;
media?.play();
}
return Play from start ;
}
```
The component re-renders when a different media attaches or the current one detaches, not when the media’s own properties change. To render from playback state, such as `paused` or `currentTime`, select it with [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player).
## The `Media` object
`Media` is the player’s handle on whatever plays the content. What that handle is depends on the media component:
- `Video` and `Audio` attach the rendered native `` or `` element.
- Components that front a playback engine or an embed, such as `HlsVideo`, `DashVideo`, or `YouTubeVideo`, attach an object that implements the media contract on top of that engine.
Because media vary this much, the `Media` type guarantees only a `play()` method and the `addEventListener`, `removeEventListener`, and `dispatchEvent` event methods. Everything else, like pausing, seeking, volume, or text tracks, is a capability that a given media may or may not have. Narrow `Media` with the [media capability guards](https://videojs.org/docs/framework/react/reference/api/media-capabilities) before using those members.
## Examples
### Basic usage
Read the attached media’s source and video dimensions, narrowing with `isMediaSourceCapable` and `isMediaVideoDimensionsCapable` first.
**App.tsx**
```tsx
import { Container, createPlayer, isMediaSourceCapable, isMediaVideoDimensionsCapable } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
const { Player, useMedia, usePlayer } = createPlayer({
features: videoFeatures,
});
function MediaInfo() {
const media = useMedia();
// `useMedia` re-renders only when the media changes; subscribe to `canPlay` so the values below refresh once loaded.
usePlayer((state) => state.canPlay);
if (!media) return null;
return (
{isMediaSourceCapable(media) && (
src
{media.currentSrc || '—'}
)}
{isMediaVideoDimensionsCapable(media) && (
<>
videoWidth
{media.videoWidth}px
videoHeight
{media.videoHeight}px
>
)}
);
}
export default function BasicUsage() {
return (
);
}
```
**App.css**
```css
.media-container {
position: relative;
}
.media-container video {
width: 100%;
}
.info-panel {
display: flex;
flex-direction: column;
gap: 4px;
padding: 12px;
margin: 0;
font-size: 0.8125rem;
background: rgba(0, 0, 0, 0.05);
border-top: 1px solid rgba(0, 0, 0, 0.1);
}
.info-panel div {
display: flex;
gap: 8px;
}
.info-panel dt {
min-width: 80px;
color: #6b7280;
}
.info-panel dd {
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
```
## API Reference
`useMedia(): Media | null`
### Return Value
| Property | Type |
| --- | --- |
| `play` | `(() => Promise)` |
| `addEventListener` | `((type: K, listener: ((event: Events[K]) => void), options?: { signal?: AbortSignal }) => void)` |
| `removeEventListener` | `((type: K, listener: ((event: Events[K]) => void)) => void)` |
| `dispatchEvent` | `((event: EventLike) => boolean)` |
---
# useContainer
Hook to access the media container from the nearest Player
`useContainer` returns the `MediaContainer` registered with the nearest `Player`. It returns `null` until the container mounts, or whenever that player has no registered container.
## Import
```tsx
import { useContainer } from "@videojs/react";
```
## Usage
Call `useContainer` from a component rendered inside `Player`:
**ContainerStatus.tsx**
```tsx
import { useContainer } from "@videojs/react";
export function ContainerStatus() {
const container = useContainer();
return {container ? "Player container ready" : "Mounting container"} ;
}
```
Calling this hook outside `Player` throws because it reads the nearest `Player`’s context. If a component can also work outside a player, use [`useOptionalContainer`](https://videojs.org/docs/framework/react/reference/api/use-optional-container) instead.
`useContainer` reads the registered container. [`useContainerAttach`](https://videojs.org/docs/framework/react/reference/api/use-container-attach) returns the setter used to register a custom container and is only needed when replacing the built-in `Container`.
## API Reference
`useContainer(): MediaContainer | null`
### Return Value
`MediaContainer | null`
---
# useOptionalContainer
Hook to access a Player container when one is available
## Import
```tsx
import { useOptionalContainer } from "@videojs/react";
```
## Usage
Use the optional hook when a component can work both inside and outside `Player`.
```tsx
function ContainerAwareStatus() {
const container = useOptionalContainer();
return {container ? "Player container ready" : "No container"} ;
}
```
It returns `null` outside `Player`, before its container mounts, or when the player has no registered container. Use `useContainer` when a missing Player should be treated as an error.
## API Reference
`useOptionalContainer(): MediaContainer | null`
### Return Value
`MediaContainer | null`
---
# useContainerAttach
Hook to register a custom container element with the player context
## Import
```tsx
import { useContainerAttach } from "@videojs/react";
```
`useContainerAttach` returns a setter function for registering a DOM element as the player’s container. The built-in `Container` uses this internally; you only need it when building a custom container element.
**CustomContainer.tsx**
```tsx
import { useContainerAttach } from "@videojs/react";
function CustomContainer({ children }: { children: React.ReactNode }) {
const setContainer = useContainerAttach();
return {children}
;
}
```
## Who needs this
You only need `useContainerAttach` if you’re replacing the built-in `Container` with a custom element. For example, if you’re building a container with custom fullscreen behavior or a non-standard layout boundary.
For standard player layouts, use the built-in `Container`.
## Safe outside Player
Returns `undefined` when called outside `Player`. Passing `undefined` to `ref` is harmless in React. Check the return value if your component needs to know whether it’s inside a player.
## API Reference
`useContainerAttach(): Dispatch> | undefined`
### Return Value
| Type |
| --- |
| `Dispatch> \| undefined` |
---
# Media capability guards
Type guards that narrow a Media object to the capabilities it supports
The media a player attaches is typed as `Media`, which guarantees only a `play()` method and the `addEventListener`, `removeEventListener`, and `dispatchEvent` event methods. A native `` element, an engine-backed media such as `HlsVideo`, and an embed such as YouTube each support a different set of everything else. Capability guards test whether a media supports one capability and narrow its type to that capability’s members.
## Import
```tsx
import { isMediaSeekCapable, isMediaVolumeCapable } from "@videojs/react";
```
Every guard on this page is a named export of the same package.
## Usage
Get the media with [`useMedia`](https://videojs.org/docs/framework/react/reference/api/use-media), then narrow it before touching anything other than `play()` and the event methods:
**HalfVolumeButton.tsx**
```tsx
import { isMediaVolumeCapable, useMedia } from "@videojs/react";
export function HalfVolumeButton() {
const media = useMedia();
if (!isMediaVolumeCapable(media)) return null;
return { media.volume = 0.5; }}>50% volume ;
}
```
The examples below assume `media` comes from `useMedia()` in a component rendered inside `Player`.
Each `isMedia*Capable` guard accepts any value and returns `false` for `null`, `undefined`, and other non-objects, so you can pass the media straight in without a separate null check.
## Behavior
A guard checks that a few members of a capability are present, then narrows to every member of that capability’s contract. The table lists both.
Some media, such as embeds, can’t provide buffered ranges, text tracks, or remote playback, and report a shared empty placeholder in their place. `isMediaBufferCapable`, `isMediaTextTrackCapable`, and `isMediaRemotePlaybackCapable` return `false` for that placeholder.
A guard describes what the media object supports, not its current state. `isMediaErrorCapable` returns `true` while `error` is `null`, and `isMediaVolumeCapable` returns `true` on platforms that ignore volume changes. For current state and availability, such as `volumeAvailability` from the [volume feature](https://videojs.org/docs/framework/react/reference/api/feature-volume), read the player’s [features](https://videojs.org/docs/framework/react/guides/features) instead.
| Guard | Checks | Narrows to |
| --- | --- | --- |
| `isMediaPauseCapable` | `paused`, `ended`, `pause()` | `pause()`, `paused`, `ended` |
| `isMediaSeekCapable` | `currentTime`, `duration`, `seeking` | `currentTime`, `loop`, `duration`, `seeking` |
| `isMediaSourceCapable` | `src`, `currentSrc`, `readyState`, `load()` | `src`, `currentSrc`, `readyState`, `preload`, `crossOrigin`, `load()`, `canPlayType()` |
| `isMediaVolumeCapable` | `volume`, `muted` | `volume`, `muted`, `defaultMuted` |
| `isMediaPlaybackRateCapable` | `playbackRate` | `playbackRate`, `defaultPlaybackRate` |
| `isMediaBufferCapable` | `buffered`, `seekable`, neither an empty placeholder | `buffered`, `seekable` |
| `isMediaErrorCapable` | `error` (`null` counts) | `error` |
| `isMediaTextTrackCapable` | `textTracks`, not an empty placeholder | `textTracks`, `addTextTrack()` |
| `isMediaVideoRenditionCapable` | `videoRenditions` | `videoRenditions` |
| `isMediaAudioTrackCapable` | `audioTracks` | `audioTracks`, `addAudioTrack()`, `removeAudioTrack()` |
| `isMediaVideoDimensionsCapable` | `videoWidth`, `videoHeight` | `videoWidth`, `videoHeight` |
| `isMediaRemotePlaybackCapable` | `remote` is an object, not an empty placeholder | `remote`, `disableRemotePlayback` |
| `isMediaStreamTypeCapable` | `streamType` | `streamType` |
| `isMediaLiveCapable` | `liveEdgeStart`, `targetLiveWindow` | `liveEdgeStart`, `targetLiveWindow` |
`hasMetadata` isn’t a type guard: it takes a media already narrowed by `isMediaSourceCapable` and returns a boolean.
## Guards
### `isMediaPauseCapable`
Narrows to `pause()` and the read-only `paused` and `ended` booleans.
```tsx
function togglePaused() {
if (!isMediaPauseCapable(media)) return;
if (media.paused) media.play();
else media.pause();
}
```
### `isMediaSeekCapable`
Narrows to the writable `currentTime` and `loop`, and the read-only `duration` and `seeking`. All times are in seconds.
```tsx
function skipForward() {
if (isMediaSeekCapable(media)) media.currentTime = Math.min(media.currentTime + 10, media.duration);
}
```
### `isMediaSourceCapable`
Narrows to the writable `src`, `preload`, and `crossOrigin`; the read-only `currentSrc` and `readyState`; and the `load()` and `canPlayType(type)` methods. `canPlayType` returns `''`, `'maybe'`, or `'probably'`.
```tsx
function playNext(url: string) {
if (!isMediaSourceCapable(media)) return;
media.src = url;
media.play();
}
```
### `hasMetadata`
Returns `true` when the media’s `readyState` is at least `HAVE_METADATA` (`1`), meaning duration and dimensions are known. It reads only `readyState`, so narrow with `isMediaSourceCapable` first.
```tsx
function resumeAt(seconds: number) {
if (!isMediaSourceCapable(media) || !hasMetadata(media)) return;
if (isMediaSeekCapable(media)) media.currentTime = seconds;
}
```
### `isMediaVolumeCapable`
Narrows to the writable `volume` (`0` to `1`), `muted`, and `defaultMuted`.
```tsx
function unmuteAtHalfVolume() {
if (!isMediaVolumeCapable(media)) return;
media.muted = false;
media.volume = 0.5;
}
```
### `isMediaPlaybackRateCapable`
Narrows to the writable `playbackRate` and `defaultPlaybackRate`.
```tsx
function playFaster() {
if (isMediaPlaybackRateCapable(media)) media.playbackRate = 1.5;
}
```
### `isMediaBufferCapable`
Narrows to the read-only `buffered` and `seekable` time ranges. Each range list has a `length` and `start(index)` and `end(index)` methods that return seconds.
```tsx
function getBufferedEnd() {
if (!isMediaBufferCapable(media) || media.buffered.length === 0) return 0;
return media.buffered.end(media.buffered.length - 1);
}
```
### `isMediaErrorCapable`
Narrows to the read-only `error`, which is `null` or an object with a numeric `code` and a `message`.
```tsx
function reportError() {
if (isMediaErrorCapable(media) && media.error) console.error(media.error.code, media.error.message);
}
```
### `isMediaTextTrackCapable`
Narrows to the read-only `textTracks` list and `addTextTrack(kind, label?, language?)`. The list is iterable and indexable, and each track has `kind`, `label`, `language`, `id`, a writable `mode` (`'showing'`, `'hidden'`, or `'disabled'`), and `cues`.
```tsx
function showCaptions(language: string) {
if (!isMediaTextTrackCapable(media)) return;
for (const track of media.textTracks) {
if (track.kind === "captions" || track.kind === "subtitles") {
track.mode = track.language === language ? "showing" : "disabled";
}
}
}
```
### `isMediaVideoRenditionCapable`
Narrows to the read-only `videoRenditions` list. The list is iterable and indexable, has `getRenditionById(id)`, and has a writable `selectedIndex`; `-1` means automatic selection. Each rendition has `id`, `width`, `height`, `bitrate`, `frameRate`, `codec`, and `selected`.
```tsx
function switchToAutomaticQuality() {
if (isMediaVideoRenditionCapable(media)) media.videoRenditions.selectedIndex = -1;
}
```
### `isMediaAudioTrackCapable`
Narrows to the read-only `audioTracks` list, `addAudioTrack(kind, label?, language?)`, and `removeAudioTrack(track)`. The list is iterable and indexable, and each track has `id`, `kind`, `label`, `language`, and a writable `enabled`.
```tsx
function selectAudioLanguage(language: string) {
if (!isMediaAudioTrackCapable(media)) return;
for (const track of media.audioTracks) track.enabled = track.language === language;
}
```
### `isMediaVideoDimensionsCapable`
Narrows to the read-only `videoWidth` and `videoHeight`, in pixels. Both are `0` until metadata loads.
```tsx
function getAspectRatio() {
if (!isMediaVideoDimensionsCapable(media) || !media.videoHeight) return null;
return media.videoWidth / media.videoHeight;
}
```
### `isMediaRemotePlaybackCapable`
Narrows to the read-only `remote` object and the writable `disableRemotePlayback`. `remote` has a `state` (`'connecting'`, `'connected'`, or `'disconnected'`), `prompt()`, `watchAvailability(callback)`, and `cancelWatchAvailability(id?)`, matching the browser’s [Remote Playback API](https://developer.mozilla.org/en-US/docs/Web/API/RemotePlayback).
```tsx
function castToDevice() {
if (isMediaRemotePlaybackCapable(media)) media.remote.prompt();
}
```
Call `prompt()` from a user gesture, such as a click handler; browsers reject it otherwise.
### `isMediaStreamTypeCapable`
Narrows to `streamType`: `'on-demand'`, `'live'`, or `'unknown'` before the type is determined.
```tsx
function isLiveStream() {
return isMediaStreamTypeCapable(media) && media.streamType === "live";
}
```
### `isMediaLiveCapable`
Narrows to the read-only `liveEdgeStart` and `targetLiveWindow`. Playback is at the live edge when `currentTime` is at or past `liveEdgeStart`, which is `NaN` when the stream isn’t live or the value is unknown. `targetLiveWindow` is `0` for a sliding live window, `Infinity` for a live event with playback history, and `NaN` for on-demand or unknown; it isn’t a duration.
```tsx
function isAtLiveEdge() {
if (!isMediaLiveCapable(media) || !isMediaSeekCapable(media)) return false;
return media.currentTime >= media.liveEdgeStart;
}
```
---
# Video preset
The general-purpose video preset — a player, feature bundle, skins, and media for on-demand video
`@videojs/react/video` is the general-purpose preset for on-demand video. It bundles `VideoPlayer`, the `videoFeatures` bundle it is configured with, two skins, and the `Video` media component. See [Presets](https://videojs.org/docs/framework/react/guides/presets) for how presets fit together and how to customize one.
## Import
```tsx
import { Video, VideoPlayer, VideoSkin } from '@videojs/react/video';
import '@videojs/react/video/skin.css';
```
Each skin has its own stylesheet. Import `@videojs/react/video/neutral-skin.css` instead when you use `NeutralVideoSkin`.
## Usage
**App.tsx**
```tsx
import { Video, VideoPlayer, VideoSkin } from '@videojs/react/video';
import '@videojs/react/video/skin.css';
export function App() {
return (
);
}
```
## Player
`VideoPlayer` is a [`Player`](https://videojs.org/docs/framework/react/reference/components/player) created by [`createPlayer`](https://videojs.org/docs/framework/react/reference/api/create-player) with `videoFeatures`. It provides the player store to its descendants and renders no DOM element of its own.
`VideoPlayerProps` types its props:
| Prop | Type | Description |
| --- | --- | --- |
| `children` | `ReactNode` | Content placed inside the player context: a skin, the media, and your own components. |
| `title` | `string \| null` | The title to display. Takes precedence over the title the media reports; `null` clears your value, so the title falls back to the one the media reports. |
| `poster` | `string \| null` | The poster to display. Takes precedence over the poster the media reports; `null` clears your value, so the poster falls back to the one the media reports. |
`title` and `poster` come from the [metadata feature](https://videojs.org/docs/framework/react/reference/api/feature-metadata#player-inputs).
The preset also exports a `usePlayer` hook typed to `videoFeatures`. Use it from any component inside `VideoPlayer`; see [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player).
## Feature bundle
`videoFeatures` is the array of [features](https://videojs.org/docs/framework/react/guides/features) the player is configured with:
| Feature | Description |
| --- | --- |
| [`playbackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-playback) | Play/pause state and actions for the player store |
| [`playbackRateFeature`](https://videojs.org/docs/framework/react/reference/api/feature-playback-rate) | Playback speed state and actions for the player store |
| [`qualityFeature`](https://videojs.org/docs/framework/react/reference/api/feature-quality) | Video rendition state and actions for the player store |
| [`audioTrackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-audio-track) | Audio track state and actions for the player store |
| [`volumeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-volume) | Volume level and mute state for the player store |
| [`timeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-time) | Playback position and duration state for the player store |
| [`sourceFeature`](https://videojs.org/docs/framework/react/reference/api/feature-source) | Media source state and actions for the player store |
| [`bufferFeature`](https://videojs.org/docs/framework/react/reference/api/feature-buffer) | Buffered and seekable time range state for the player store |
| [`fullscreenFeature`](https://videojs.org/docs/framework/react/reference/api/feature-fullscreen) | Fullscreen state and actions for the player store |
| [`pipFeature`](https://videojs.org/docs/framework/react/reference/api/feature-pip) | Picture-in-picture state and actions for the player store |
| [`remotePlaybackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-remote-playback) | Remote playback state and actions for the player store |
| [`controlsFeature`](https://videojs.org/docs/framework/react/reference/api/feature-controls) | User activity and controls visibility state for the player store |
| [`textTrackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-text-tracks) | Subtitles, captions, and chapter track state for the player store |
| [`errorFeature`](https://videojs.org/docs/framework/react/reference/api/feature-error) | Media error state and actions for the player store |
| [`metadataFeature`](https://videojs.org/docs/framework/react/reference/api/feature-metadata) | Resolved title and poster values for the player store |
Its type, `VideoFeatures`, is the tuple of those features in order. Import it from the package root:
```tsx
import type { VideoFeatures } from '@videojs/react';
```
To add or remove a feature, pass your own array to `createPlayer`. The skins expect the features they render controls for.
```tsx
import { createPlayer, orientationLockFeature } from '@videojs/react';
import { videoFeatures } from '@videojs/react/video';
const { Player, usePlayer } = createPlayer({
features: [...videoFeatures, orientationLockFeature],
});
```
## Skins
- [VideoSkin](https://videojs.org/docs/framework/react/reference/components/video-skin) — the default skin, with the modern, frosted look.
- [NeutralVideoSkin](https://videojs.org/docs/framework/react/reference/components/video-neutral-skin) — the visually lighter variant, closer to a classic control bar.
## Media
The preset exports [`Video`](https://videojs.org/docs/framework/react/reference/components/video), which renders a native ``. Swap it for any compatible [media component](https://videojs.org/docs/framework/react/guides/media-sources), such as [`HlsJsVideo`](https://videojs.org/docs/framework/react/reference/components/hlsjs-video) for HLS.
## Exports
`@videojs/react/video` exports:
| Export | Description |
| --- | --- |
| `VideoPlayer`, `VideoPlayerProps` | The preconfigured [player](https://videojs.org/docs/framework/react/reference/api/preset-video#player). |
| `usePlayer` | The player-store hook, typed to `videoFeatures`. |
| `videoFeatures` | The [feature bundle](https://videojs.org/docs/framework/react/reference/api/preset-video#feature-bundle). |
| `VideoSkin`, `VideoSkinProps` | The default [skin](https://videojs.org/docs/framework/react/reference/api/preset-video#skins). |
| `NeutralVideoSkin`, `NeutralVideoSkinProps` | The neutral [skin](https://videojs.org/docs/framework/react/reference/api/preset-video#skins). |
| `Video`, `VideoProps` | The [media](https://videojs.org/docs/framework/react/reference/api/preset-video#media) component. |
Stylesheets: `@videojs/react/video/skin.css` and `@videojs/react/video/neutral-skin.css`.
---
# Audio preset
The audio-only preset — a player, feature bundle, skins, and media for on-demand audio
`@videojs/react/audio` is the preset for on-demand audio. It bundles `AudioPlayer`, the `audioFeatures` bundle it is configured with, two skins, and the `Audio` media component. See [Presets](https://videojs.org/docs/framework/react/guides/presets) for how presets fit together and how to customize one.
## Import
```tsx
import { Audio, AudioPlayer, AudioSkin } from '@videojs/react/audio';
import '@videojs/react/audio/skin.css';
```
Each skin has its own stylesheet. Import `@videojs/react/audio/neutral-skin.css` instead when you use `NeutralAudioSkin`.
## Usage
**App.tsx**
```tsx
import { Audio, AudioPlayer, AudioSkin } from '@videojs/react/audio';
import '@videojs/react/audio/skin.css';
export function App() {
return (
);
}
```
## Player
`AudioPlayer` is a [`Player`](https://videojs.org/docs/framework/react/reference/components/player) created by [`createPlayer`](https://videojs.org/docs/framework/react/reference/api/create-player) with `audioFeatures`. It provides the player store to its descendants and renders no DOM element of its own.
`AudioPlayerProps` types its props:
| Prop | Type | Description |
| --- | --- | --- |
| `children` | `ReactNode` | Content placed inside the player context: a skin, the media, and your own components. |
| `title` | `string \| null` | The title to display. Takes precedence over the title the media reports; `null` clears your value, so the title falls back to the one the media reports. |
| `poster` | `string \| null` | The poster to display. Takes precedence over the poster the media reports; `null` clears your value, so the poster falls back to the one the media reports. |
`title` and `poster` come from the [metadata feature](https://videojs.org/docs/framework/react/reference/api/feature-metadata#player-inputs).
The preset also exports a `usePlayer` hook typed to `audioFeatures`. Use it from any component inside `AudioPlayer`; see [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player).
## Feature bundle
`audioFeatures` is the array of [features](https://videojs.org/docs/framework/react/guides/features) the player is configured with, a subset of the [video preset’s](https://videojs.org/docs/framework/react/reference/api/preset-video#feature-bundle) `videoFeatures`:
| Feature | Description |
| --- | --- |
| [`playbackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-playback) | Play/pause state and actions for the player store |
| [`playbackRateFeature`](https://videojs.org/docs/framework/react/reference/api/feature-playback-rate) | Playback speed state and actions for the player store |
| [`volumeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-volume) | Volume level and mute state for the player store |
| [`timeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-time) | Playback position and duration state for the player store |
| [`sourceFeature`](https://videojs.org/docs/framework/react/reference/api/feature-source) | Media source state and actions for the player store |
| [`bufferFeature`](https://videojs.org/docs/framework/react/reference/api/feature-buffer) | Buffered and seekable time range state for the player store |
| [`errorFeature`](https://videojs.org/docs/framework/react/reference/api/feature-error) | Media error state and actions for the player store |
| [`metadataFeature`](https://videojs.org/docs/framework/react/reference/api/feature-metadata) | Resolved title and poster values for the player store |
Its type, `AudioFeatures`, is the tuple of those features in order. Import it from the package root:
```tsx
import type { AudioFeatures } from '@videojs/react';
```
To add or remove a feature, pass your own array to `createPlayer`. The skins expect the features they render controls for.
```tsx
import { createPlayer, remotePlaybackFeature } from '@videojs/react';
import { audioFeatures } from '@videojs/react/audio';
const { Player, usePlayer } = createPlayer({
features: [...audioFeatures, remotePlaybackFeature],
});
```
## Skins
- [AudioSkin](https://videojs.org/docs/framework/react/reference/components/audio-skin) — the default skin, with the modern, frosted look.
- [NeutralAudioSkin](https://videojs.org/docs/framework/react/reference/components/audio-neutral-skin) — the visually lighter variant, closer to a classic control bar.
## Media
The preset exports [`Audio`](https://videojs.org/docs/framework/react/reference/components/audio), which renders a native ``. Swap it for any compatible [media component](https://videojs.org/docs/framework/react/guides/media-sources), such as [`HlsAudio`](https://videojs.org/docs/framework/react/reference/components/hls-audio) for HLS.
## Exports
`@videojs/react/audio` exports:
| Export | Description |
| --- | --- |
| `AudioPlayer`, `AudioPlayerProps` | The preconfigured [player](https://videojs.org/docs/framework/react/reference/api/preset-audio#player). |
| `usePlayer` | The player-store hook, typed to `audioFeatures`. |
| `audioFeatures` | The [feature bundle](https://videojs.org/docs/framework/react/reference/api/preset-audio#feature-bundle). |
| `AudioSkin`, `AudioSkinProps` | The default [skin](https://videojs.org/docs/framework/react/reference/api/preset-audio#skins). |
| `NeutralAudioSkin`, `NeutralAudioSkinProps` | The neutral [skin](https://videojs.org/docs/framework/react/reference/api/preset-audio#skins). |
| `Audio`, `AudioProps` | The [media](https://videojs.org/docs/framework/react/reference/api/preset-audio#media) component. |
Stylesheets: `@videojs/react/audio/skin.css` and `@videojs/react/audio/neutral-skin.css`.
---
# Live video preset
The live video preset — a player, feature bundle, skins, and media for live video streams
`@videojs/react/live-video` is the preset for live video streams. It bundles `LiveVideoPlayer`, the `liveVideoFeatures` bundle it is configured with, two skins that swap the time slider and time displays for a Live button, and the `Video` media component. See [Presets](https://videojs.org/docs/framework/react/guides/presets) for how presets fit together and [Live streams](https://videojs.org/docs/framework/react/guides/live-streams) for playing a live source.
## Import
```tsx
import { LiveVideoPlayer, LiveVideoSkin, Video } from '@videojs/react/live-video';
import '@videojs/react/live-video/skin.css';
```
Each skin has its own stylesheet. Import `@videojs/react/live-video/neutral-skin.css` instead when you use `NeutralLiveVideoSkin`.
## Usage
Live streams are usually HLS, so this example plays through an HLS media component instead of the preset’s native video.
**App.tsx**
```tsx
import { HlsJsVideo } from '@videojs/react/media/hlsjs-video';
import { LiveVideoPlayer, LiveVideoSkin } from '@videojs/react/live-video';
import '@videojs/react/live-video/skin.css';
export function App() {
return (
);
}
```
## Player
`LiveVideoPlayer` is a [`Player`](https://videojs.org/docs/framework/react/reference/components/player) created by [`createPlayer`](https://videojs.org/docs/framework/react/reference/api/create-player) with `liveVideoFeatures`. It provides the player store to its descendants and renders no DOM element of its own.
`LiveVideoPlayerProps` types its props:
| Prop | Type | Description |
| --- | --- | --- |
| `children` | `ReactNode` | Content placed inside the player context: a skin, the media, and your own components. |
| `title` | `string \| null` | The title to display. Takes precedence over the title the media reports; `null` clears your value, so the title falls back to the one the media reports. |
| `poster` | `string \| null` | The poster to display. Takes precedence over the poster the media reports; `null` clears your value, so the poster falls back to the one the media reports. |
`title` and `poster` come from the [metadata feature](https://videojs.org/docs/framework/react/reference/api/feature-metadata#player-inputs).
The preset also exports a `usePlayer` hook typed to `liveVideoFeatures`. Use it from any component inside `LiveVideoPlayer`; see [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player).
## Feature bundle
`liveVideoFeatures` is the array of [features](https://videojs.org/docs/framework/react/guides/features) the player is configured with. It is the [video preset’s](https://videojs.org/docs/framework/react/reference/api/preset-video#feature-bundle) `videoFeatures` without playback rate, quality, and audio-track selection, plus the [live feature](https://videojs.org/docs/framework/react/reference/api/feature-live):
| Feature | Description |
| --- | --- |
| [`playbackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-playback) | Play/pause state and actions for the player store |
| [`volumeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-volume) | Volume level and mute state for the player store |
| [`timeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-time) | Playback position and duration state for the player store |
| [`sourceFeature`](https://videojs.org/docs/framework/react/reference/api/feature-source) | Media source state and actions for the player store |
| [`bufferFeature`](https://videojs.org/docs/framework/react/reference/api/feature-buffer) | Buffered and seekable time range state for the player store |
| [`fullscreenFeature`](https://videojs.org/docs/framework/react/reference/api/feature-fullscreen) | Fullscreen state and actions for the player store |
| [`pipFeature`](https://videojs.org/docs/framework/react/reference/api/feature-pip) | Picture-in-picture state and actions for the player store |
| [`remotePlaybackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-remote-playback) | Remote playback state and actions for the player store |
| [`controlsFeature`](https://videojs.org/docs/framework/react/reference/api/feature-controls) | User activity and controls visibility state for the player store |
| [`textTrackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-text-tracks) | Subtitles, captions, and chapter track state for the player store |
| [`errorFeature`](https://videojs.org/docs/framework/react/reference/api/feature-error) | Media error state and actions for the player store |
| [`liveFeature`](https://videojs.org/docs/framework/react/reference/api/feature-live) | Live edge state for the player store |
| [`metadataFeature`](https://videojs.org/docs/framework/react/reference/api/feature-metadata) | Resolved title and poster values for the player store |
Its type, `LiveVideoFeatures`, is the tuple of those features in order. Import it from the package root:
```tsx
import type { LiveVideoFeatures } from '@videojs/react';
```
To add or remove a feature, pass your own array to `createPlayer`. The skins expect the features they render controls for.
```tsx
import { createPlayer, streamTypeFeature } from '@videojs/react';
import { liveVideoFeatures } from '@videojs/react/live-video';
const { Player, usePlayer } = createPlayer({
features: [...liveVideoFeatures, streamTypeFeature],
});
```
## Skins
- [LiveVideoSkin](https://videojs.org/docs/framework/react/reference/components/live-video-skin) — the default skin, which swaps the time controls for a Live button.
- [NeutralLiveVideoSkin](https://videojs.org/docs/framework/react/reference/components/live-video-neutral-skin) — the visually lighter variant, closer to a classic control bar.
## Media
The preset exports [`Video`](https://videojs.org/docs/framework/react/reference/components/video), the same component as the video preset, which renders a native ``. Most browsers need a streaming [media component](https://videojs.org/docs/framework/react/guides/media-sources) to play a live source, such as [`HlsJsVideo`](https://videojs.org/docs/framework/react/reference/components/hlsjs-video) for HLS.
## Exports
`@videojs/react/live-video` exports:
| Export | Description |
| --- | --- |
| `LiveVideoPlayer`, `LiveVideoPlayerProps` | The preconfigured [player](https://videojs.org/docs/framework/react/reference/api/preset-live-video#player). |
| `usePlayer` | The player-store hook, typed to `liveVideoFeatures`. |
| `liveVideoFeatures` | The [feature bundle](https://videojs.org/docs/framework/react/reference/api/preset-live-video#feature-bundle). |
| `LiveVideoSkin`, `LiveVideoSkinProps` | The default [skin](https://videojs.org/docs/framework/react/reference/api/preset-live-video#skins). |
| `NeutralLiveVideoSkin`, `NeutralLiveVideoSkinProps` | The neutral [skin](https://videojs.org/docs/framework/react/reference/api/preset-live-video#skins). |
| `Video`, `VideoProps` | The [media](https://videojs.org/docs/framework/react/reference/api/preset-live-video#media) component. |
Stylesheets: `@videojs/react/live-video/skin.css` and `@videojs/react/live-video/neutral-skin.css`.
---
# Live audio preset
The live audio preset — a player, feature bundle, skins, and media for live audio streams
`@videojs/react/live-audio` is the preset for live audio streams. It bundles `LiveAudioPlayer`, the `liveAudioFeatures` bundle it is configured with, two skins that swap the time slider and time displays for a Live button, and the `Audio` media component. See [Presets](https://videojs.org/docs/framework/react/guides/presets) for how presets fit together and [Live streams](https://videojs.org/docs/framework/react/guides/live-streams) for playing a live source.
## Import
```tsx
import { Audio, LiveAudioPlayer, LiveAudioSkin } from '@videojs/react/live-audio';
import '@videojs/react/live-audio/skin.css';
```
Each skin has its own stylesheet. Import `@videojs/react/live-audio/neutral-skin.css` instead when you use `NeutralLiveAudioSkin`.
## Usage
Live streams are usually HLS, so this example plays through an HLS media component instead of the preset’s native audio.
**App.tsx**
```tsx
import { HlsAudio } from '@videojs/react/media/hls-audio';
import { LiveAudioPlayer, LiveAudioSkin } from '@videojs/react/live-audio';
import '@videojs/react/live-audio/skin.css';
export function App() {
return (
);
}
```
## Player
`LiveAudioPlayer` is a [`Player`](https://videojs.org/docs/framework/react/reference/components/player) created by [`createPlayer`](https://videojs.org/docs/framework/react/reference/api/create-player) with `liveAudioFeatures`. It provides the player store to its descendants and renders no DOM element of its own.
`LiveAudioPlayerProps` types its props:
| Prop | Type | Description |
| --- | --- | --- |
| `children` | `ReactNode` | Content placed inside the player context: a skin, the media, and your own components. |
| `title` | `string \| null` | The title to display. Takes precedence over the title the media reports; `null` clears your value, so the title falls back to the one the media reports. |
| `poster` | `string \| null` | The poster to display. Takes precedence over the poster the media reports; `null` clears your value, so the poster falls back to the one the media reports. |
`title` and `poster` come from the [metadata feature](https://videojs.org/docs/framework/react/reference/api/feature-metadata#player-inputs).
The preset also exports a `usePlayer` hook typed to `liveAudioFeatures`. Use it from any component inside `LiveAudioPlayer`; see [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player).
## Feature bundle
`liveAudioFeatures` is the array of [features](https://videojs.org/docs/framework/react/guides/features) the player is configured with. It is the [audio preset’s](https://videojs.org/docs/framework/react/reference/api/preset-audio#feature-bundle) `audioFeatures` without playback rate, plus the [live feature](https://videojs.org/docs/framework/react/reference/api/feature-live):
| Feature | Description |
| --- | --- |
| [`playbackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-playback) | Play/pause state and actions for the player store |
| [`volumeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-volume) | Volume level and mute state for the player store |
| [`timeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-time) | Playback position and duration state for the player store |
| [`sourceFeature`](https://videojs.org/docs/framework/react/reference/api/feature-source) | Media source state and actions for the player store |
| [`bufferFeature`](https://videojs.org/docs/framework/react/reference/api/feature-buffer) | Buffered and seekable time range state for the player store |
| [`errorFeature`](https://videojs.org/docs/framework/react/reference/api/feature-error) | Media error state and actions for the player store |
| [`liveFeature`](https://videojs.org/docs/framework/react/reference/api/feature-live) | Live edge state for the player store |
| [`metadataFeature`](https://videojs.org/docs/framework/react/reference/api/feature-metadata) | Resolved title and poster values for the player store |
Its type, `LiveAudioFeatures`, is the tuple of those features in order. Import it from the package root:
```tsx
import type { LiveAudioFeatures } from '@videojs/react';
```
To add or remove a feature, pass your own array to `createPlayer`. The skins expect the features they render controls for.
```tsx
import { createPlayer, streamTypeFeature } from '@videojs/react';
import { liveAudioFeatures } from '@videojs/react/live-audio';
const { Player, usePlayer } = createPlayer({
features: [...liveAudioFeatures, streamTypeFeature],
});
```
## Skins
- [LiveAudioSkin](https://videojs.org/docs/framework/react/reference/components/live-audio-skin) — the default skin, which swaps the time controls for a Live button.
- [NeutralLiveAudioSkin](https://videojs.org/docs/framework/react/reference/components/live-audio-neutral-skin) — the visually lighter variant, closer to a classic control bar.
## Media
The preset exports [`Audio`](https://videojs.org/docs/framework/react/reference/components/audio), the same component as the audio preset, which renders a native ``. Most browsers need a streaming [media component](https://videojs.org/docs/framework/react/guides/media-sources) to play a live source, such as [`HlsAudio`](https://videojs.org/docs/framework/react/reference/components/hls-audio) for HLS.
## Exports
`@videojs/react/live-audio` exports:
| Export | Description |
| --- | --- |
| `LiveAudioPlayer`, `LiveAudioPlayerProps` | The preconfigured [player](https://videojs.org/docs/framework/react/reference/api/preset-live-audio#player). |
| `usePlayer` | The player-store hook, typed to `liveAudioFeatures`. |
| `liveAudioFeatures` | The [feature bundle](https://videojs.org/docs/framework/react/reference/api/preset-live-audio#feature-bundle). |
| `LiveAudioSkin`, `LiveAudioSkinProps` | The default [skin](https://videojs.org/docs/framework/react/reference/api/preset-live-audio#skins). |
| `NeutralLiveAudioSkin`, `NeutralLiveAudioSkinProps` | The neutral [skin](https://videojs.org/docs/framework/react/reference/api/preset-live-audio#skins). |
| `Audio`, `AudioProps` | The [media](https://videojs.org/docs/framework/react/reference/api/preset-live-audio#media) component. |
Stylesheets: `@videojs/react/live-audio/skin.css` and `@videojs/react/live-audio/neutral-skin.css`.
---
# Background preset
The background video preset — a player, feature bundle, skin, and media for ambient video with no controls
`@videojs/react/background` is the preset for ambient background video with no user controls. It bundles `BackgroundVideoPlayer`, the `backgroundFeatures` bundle it is configured with, one chrome-less skin, and the `BackgroundVideo` media component, which autoplays, mutes, and loops. See [Presets](https://videojs.org/docs/framework/react/guides/presets) for how presets fit together and [Background video](https://videojs.org/docs/framework/react/guides/background-video) for building one.
## Import
```tsx
import { BackgroundVideo, BackgroundVideoPlayer, BackgroundVideoSkin } from '@videojs/react/background';
import '@videojs/react/background/skin.css';
```
## Usage
**App.tsx**
```tsx
import { BackgroundVideo, BackgroundVideoPlayer, BackgroundVideoSkin } from '@videojs/react/background';
import '@videojs/react/background/skin.css';
export function Hero() {
return (
);
}
```
## Player
`BackgroundVideoPlayer` is a [`Player`](https://videojs.org/docs/framework/react/reference/components/player) created by [`createPlayer`](https://videojs.org/docs/framework/react/reference/api/create-player) with `backgroundFeatures`. It provides the player store to its descendants and renders no DOM element of its own.
| Prop | Type | Description |
| --- | --- | --- |
| `children` | `ReactNode` | Content placed inside the player context: the skin, the media, and your own components. |
The bundle has no feature that takes player inputs, so `children` is the only prop. Unlike the other presets, this one exports no named props type.
The preset also exports a `usePlayer` hook typed to `backgroundFeatures`. Use it from any component inside `BackgroundVideoPlayer`; see [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player).
## Feature bundle
`backgroundFeatures` is the array of [features](https://videojs.org/docs/framework/react/guides/features) the player is configured with. The background media component handles autoplay, muting, and looping on its own:
`backgroundFeatures` is currently empty, so the player's store holds no feature state or actions.
Its type, `BackgroundFeatures`, is the matching empty tuple. Import it from the package root:
```tsx
import type { BackgroundFeatures } from '@videojs/react';
```
To add a feature, pass your own array to `createPlayer`. For example, add playback to drive a play button over the background video:
```tsx
import { createPlayer, playbackFeature } from '@videojs/react';
import { backgroundFeatures } from '@videojs/react/background';
const { Player, usePlayer } = createPlayer({
features: [...backgroundFeatures, playbackFeature],
});
```
## Skins
- [BackgroundVideoSkin](https://videojs.org/docs/framework/react/reference/components/background-video-skin) — the only skin, a chrome-less surface that sizes the video and renders no controls.
## Media
The preset exports [`BackgroundVideo`](https://videojs.org/docs/framework/react/reference/components/background-video), which renders a native `` that is muted, looped, and autoplaying by default. For HLS or Mux sources, use [`HlsBackgroundVideo`](https://videojs.org/docs/framework/react/reference/components/hls-background-video) or [`MuxBackgroundVideo`](https://videojs.org/docs/framework/react/reference/components/mux-background-video) instead.
## Exports
`@videojs/react/background` exports:
| Export | Description |
| --- | --- |
| `BackgroundVideoPlayer` | The preconfigured [player](https://videojs.org/docs/framework/react/reference/api/preset-background#player). |
| `usePlayer` | The player-store hook, typed to `backgroundFeatures`. |
| `backgroundFeatures` | The [feature bundle](https://videojs.org/docs/framework/react/reference/api/preset-background#feature-bundle). |
| `BackgroundVideoSkin`, `BackgroundVideoSkinProps` | The [skin](https://videojs.org/docs/framework/react/reference/api/preset-background#skins). |
| `BackgroundVideo`, `BackgroundVideoProps` | The [media](https://videojs.org/docs/framework/react/reference/api/preset-background#media) component. |
Stylesheet: `@videojs/react/background/skin.css`.
---
# Player store
Every state field and action on the player store, grouped by the feature that adds it
The player store combines the state and actions of every feature the player includes. A field or action exists only when its feature is included. This page lists the whole store; each feature’s reference page adds its selector and examples.
## Access
[`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) returns the store without a selector and the selected state with one. State fields and actions are properties of the store.
```tsx
const store = usePlayer();
const paused = usePlayer((s) => s.paused);
store.play();
```
Each feature also exports a selector, such as `selectPlayback`, that returns its state and actions, or `undefined` when the player doesn’t include the feature. See [`createSelector`](https://videojs.org/docs/framework/react/reference/api/create-selector) to write your own.
## State and actions
Each feature links to the same entry on its reference page.
| Name | Type | Feature | Description |
| --- | --- | --- | --- |
| `paused` | `boolean` | [`playbackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-playback#playback-state-paused) | Whether playback is paused. |
| `ended` | `boolean` | [`playbackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-playback#playback-state-ended) | Whether playback has reached the end. |
| `started` | `boolean` | [`playbackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-playback#playback-state-started) | Whether playback has started (played or seeked). |
| `waiting` | `boolean` | [`playbackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-playback#playback-state-waiting) | Whether playback is stalled waiting for data. |
| `play` | `() => Promise` | [`playbackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-playback#playback-action-play) | Start playback. Updates `paused` immediately when the media starts. |
| `pause` | `() => void` | [`playbackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-playback#playback-action-pause) | Pause playback. Updates `paused` immediately. |
| `currentTime` | `number` | [`timeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-time#time-state-currentTime) | Current playback position in seconds. |
| `duration` | `number` | [`timeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-time#time-state-duration) | Total duration in seconds (0 if unknown). |
| `seeking` | `boolean` | [`timeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-time#time-state-seeking) | Whether a seek operation is in progress. |
| `seek` | `(time: number) => Promise` | [`timeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-time#time-action-seek) | Seek to a time in seconds. Returns the actual position after seek. |
| `volume` | `number` | [`volumeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-volume#volume-state-volume) | Volume level from 0 (silent) to 1 (max). |
| `muted` | `boolean` | [`volumeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-volume#volume-state-muted) | Whether audio is muted. |
| `volumeAvailability` | `'available' \| 'unavailable' \| 'unsupported'` | [`volumeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-volume#volume-state-volumeAvailability) | Whether volume can be programmatically set on this platform. |
| `mutedAvailability` | `'available' \| 'unavailable' \| 'unsupported'` | [`volumeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-volume#volume-state-mutedAvailability) | Whether the media can be muted. Separate from `volumeAvailability` because the two come apart: an embed can take a mute command while offering no way to set a level, and iOS Safari refuses a volume write on media that mutes perfectly well. |
| `setVolume` | `(volume: number) => number` | [`volumeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-volume#volume-action-setVolume) | Set volume (clamped 0-1). Returns the clamped value. |
| `setMuted` | `(muted: boolean) => boolean` | [`volumeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-volume#volume-action-setMuted) | Set the muted state, updating the store immediately. Unmuting at volume 0 restores volume to 0.25. Returns the new muted value. |
| `isFullscreen` | `boolean` | [`fullscreenFeature`](https://videojs.org/docs/framework/react/reference/api/feature-fullscreen#fullscreen-state-isFullscreen) | Whether fullscreen mode is currently active. |
| `fullscreenAvailability` | `'available' \| 'unavailable' \| 'unsupported'` | [`fullscreenFeature`](https://videojs.org/docs/framework/react/reference/api/feature-fullscreen#fullscreen-state-fullscreenAvailability) | Whether fullscreen can be requested on this platform. |
| `requestFullscreen` | `() => Promise` | [`fullscreenFeature`](https://videojs.org/docs/framework/react/reference/api/feature-fullscreen#fullscreen-action-requestFullscreen) | Enter fullscreen mode. Tries container first, falls back to media component. |
| `exitFullscreen` | `() => Promise` | [`fullscreenFeature`](https://videojs.org/docs/framework/react/reference/api/feature-fullscreen#fullscreen-action-exitFullscreen) | Exit fullscreen mode. |
| `userActive` | `boolean` | [`controlsFeature`](https://videojs.org/docs/framework/react/reference/api/feature-controls#controls-state-userActive) | Whether the user has recently interacted with the player. |
| `controlsVisible` | `boolean` | [`controlsFeature`](https://videojs.org/docs/framework/react/reference/api/feature-controls#controls-state-controlsVisible) | Whether controls should be visible. |
| `requestControlsLock` | `() => (() => void)` | [`controlsFeature`](https://videojs.org/docs/framework/react/reference/api/feature-controls#controls-action-requestControlsLock) | Keep controls visible during a sustained interaction. The returned function releases the lock. Multiple concurrent locks are supported and each release function is idempotent. |
| `toggleControls` | `(forceShow?: boolean) => boolean` | [`controlsFeature`](https://videojs.org/docs/framework/react/reference/api/feature-controls#controls-action-toggleControls) | Toggle controls visibility, or force it with `forceShow`. Returns the new `controlsVisible` value. |
| `buffered` | `[number, number][]` | [`bufferFeature`](https://videojs.org/docs/framework/react/reference/api/feature-buffer#buffer-state-buffered) | Buffered time ranges as \[start, end\] tuples. |
| `seekable` | `[number, number][]` | [`bufferFeature`](https://videojs.org/docs/framework/react/reference/api/feature-buffer#buffer-state-seekable) | Seekable time ranges as \[start, end\] tuples. |
| `textTrackList` | `MediaTextTrack[]` | [`textTrackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-text-tracks#textTrack-state-textTrackList) | All text tracks available on the media component. |
| `subtitlesShowing` | `boolean` | [`textTrackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-text-tracks#textTrack-state-subtitlesShowing) | Whether a captions/subtitles track is showing. |
| `chaptersCues` | `MediaTextCue[]` | [`textTrackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-text-tracks#textTrack-state-chaptersCues) | Cues from the first `kind="chapters"` track, with cue ends clamped to a finite media duration. |
| `thumbnailsTrack` | `{ cues: MediaTextCue[]; src: string \| null; crossOrigin: 'anonymous' \| 'use-credentials' \| null } \| null` | [`textTrackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-text-tracks#textTrack-state-thumbnailsTrack) | The first thumbnails track, or `null` when there is none. |
| `toggleSubtitles` | `(forceShow?: boolean) => boolean` | [`textTrackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-text-tracks#textTrack-action-toggleSubtitles) | Toggle captions/subtitles visibility. Showing enables one caption/subtitle track. A track already showing stays selected. Otherwise, selection prefers the last track shown, then a track matching the browser language, then the first available track. Returns whether a track is showing. |
| `selectSubtitlesTrack` | `(id: string \| null) => void` | [`textTrackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-text-tracks#textTrack-action-selectSubtitlesTrack) | Show the captions/subtitles track with `id`, or turn captions/subtitles off with `null`. |
| `playbackRates` | `readonly number[]` | [`playbackRateFeature`](https://videojs.org/docs/framework/react/reference/api/feature-playback-rate#playbackRate-state-playbackRates) | Available playback rates. |
| `playbackRate` | `number` | [`playbackRateFeature`](https://videojs.org/docs/framework/react/reference/api/feature-playback-rate#playbackRate-state-playbackRate) | Current playback rate. |
| `setPlaybackRate` | `(rate: number) => void` | [`playbackRateFeature`](https://videojs.org/docs/framework/react/reference/api/feature-playback-rate#playbackRate-action-setPlaybackRate) | Set the playback rate. |
| `isPictureInPicture` | `boolean` | [`pipFeature`](https://videojs.org/docs/framework/react/reference/api/feature-pip#pip-state-isPictureInPicture) | Whether picture-in-picture mode is currently active. |
| `pictureInPictureAvailability` | `'available' \| 'unavailable' \| 'unsupported'` | [`pipFeature`](https://videojs.org/docs/framework/react/reference/api/feature-pip#pip-state-pictureInPictureAvailability) | Whether picture-in-picture can be requested on this platform. |
| `requestPictureInPicture` | `() => Promise` | [`pipFeature`](https://videojs.org/docs/framework/react/reference/api/feature-pip#pip-action-requestPictureInPicture) | Enter picture-in-picture mode, exiting fullscreen first. Rejects before metadata is loaded. |
| `exitPictureInPicture` | `() => Promise` | [`pipFeature`](https://videojs.org/docs/framework/react/reference/api/feature-pip#pip-action-exitPictureInPicture) | Exit picture-in-picture mode. |
| `error` | `{ code: number; message: string } \| null` | [`errorFeature`](https://videojs.org/docs/framework/react/reference/api/feature-error#error-state-error) | The current media error, or null if none. |
| `dismissError` | `() => void` | [`errorFeature`](https://videojs.org/docs/framework/react/reference/api/feature-error#error-action-dismissError) | Dismiss the current error by clearing it. |
| `title` | `string` | [`metadataFeature`](https://videojs.org/docs/framework/react/reference/api/feature-metadata#metadata-state-title) | The resolved content title. Set it through the player, not through the store. |
| `poster` | `string` | [`metadataFeature`](https://videojs.org/docs/framework/react/reference/api/feature-metadata#metadata-state-poster) | The resolved poster URL, independent of the media component's own `poster`. Set it through the player, not through the store. |
| `currentSrc` | `string` | [`sourceFeature`](https://videojs.org/docs/framework/react/reference/api/feature-source#source-state-currentSrc) | Current media source URL (empty string if none). |
| `canPlay` | `boolean` | [`sourceFeature`](https://videojs.org/docs/framework/react/reference/api/feature-source#source-state-canPlay) | Whether enough data is loaded to begin playback. |
| `videoRenditionList` | `MediaVideoRendition[]` | [`qualityFeature`](https://videojs.org/docs/framework/react/reference/api/feature-quality#quality-state-videoRenditionList) | Video renditions available for manual quality selection. |
| `activeVideoRendition` | `{ id: string; width?: number; height?: number; bitrate?: number; frameRate?: number; codec?: string; selected: boolean } \| null` | [`qualityFeature`](https://videojs.org/docs/framework/react/reference/api/feature-quality#quality-state-activeVideoRendition) | Video rendition currently playing, including when automatic ABR is selected. |
| `selectVideoRendition` | `(id: string) => void` | [`qualityFeature`](https://videojs.org/docs/framework/react/reference/api/feature-quality#quality-action-selectVideoRendition) | Select a video rendition by `id`, or automatic ABR with `"auto"`. |
| `audioTrackList` | `MediaAudioTrack[]` | [`audioTrackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-audio-track#audioTrack-state-audioTrackList) | Audio tracks available for manual track selection. |
| `selectAudioTrack` | `(id: string) => void` | [`audioTrackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-audio-track#audioTrack-action-selectAudioTrack) | Select an audio track by `id`. |
| `remotePlaybackState` | `'disconnected' \| 'connecting' \| 'connected'` | [`remotePlaybackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-remote-playback#remotePlayback-state-remotePlaybackState) | Current remote playback connection state. |
| `remotePlaybackAvailability` | `'available' \| 'unavailable' \| 'unsupported'` | [`remotePlaybackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-remote-playback#remotePlayback-state-remotePlaybackAvailability) | Whether remote playback can be requested on this platform. |
| `promptRemotePlayback` | `() => Promise` | [`remotePlaybackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-remote-playback#remotePlayback-action-promptRemotePlayback) | Prompt the user to pick a remote playback device. Exits fullscreen first when connecting. |
| `liveEdgeStart` | `number` | [`liveFeature`](https://videojs.org/docs/framework/react/reference/api/feature-live#live-state-liveEdgeStart) | Playback time where the live edge begins. Playback is live when `currentTime >= liveEdgeStart`. `NaN` when the stream is not live or the value is unknown. |
| `targetLiveWindow` | `number` | [`liveFeature`](https://videojs.org/docs/framework/react/reference/api/feature-live#live-state-targetLiveWindow) | Describes the kind of live window available. This value is not a duration. `0` for a sliding live window, `Infinity` for a live event with playback history, and `NaN` for on-demand or unknown. |
| `streamType` | `'on-demand' \| 'live' \| 'unknown'` | [`streamTypeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-stream-type#streamType-state-streamType) | Current stream delivery type. Components use this to show live-specific UI (for example, a live indicator or a "jump to live edge" button) or hide the time display. |
| `orientationLockType` | `'any' \| 'landscape' \| 'landscape-primary' \| 'landscape-secondary' \| 'natural' \| 'portrait' \| 'portrait-primary' \| 'portrait-secondary'` | [`orientationLockFeature`](https://videojs.org/docs/framework/react/reference/api/feature-orientation-lock#orientationLock-state-orientationLockType) | Screen orientation type locked while fullscreen is active. |
| `setOrientationLockType` | `(value: 'any' \| 'landscape' \| 'landscape-primary' \| 'landscape-secondary' \| 'natural' \| 'portrait' \| 'portrait-primary' \| 'portrait-secondary' \| null \| undefined) => void` | [`orientationLockFeature`](https://videojs.org/docs/framework/react/reference/api/feature-orientation-lock#orientationLock-action-setOrientationLockType) | Sets the locked orientation type. A missing value, including an empty one, restores the default. |
## Features by preset
Each column is a preset's feature bundle. A feature no bundle includes is opt-in: add it to your [`createPlayer`](https://videojs.org/docs/framework/react/reference/api/create-player) features yourself.
| Feature | `videoFeatures` | `audioFeatures` | `liveAudioFeatures` | `liveVideoFeatures` |
| --- | --- | --- | --- | --- |
| [`playbackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-playback) | ✓ | ✓ | ✓ | ✓ |
| [`timeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-time) | ✓ | ✓ | ✓ | ✓ |
| [`volumeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-volume) | ✓ | ✓ | ✓ | ✓ |
| [`fullscreenFeature`](https://videojs.org/docs/framework/react/reference/api/feature-fullscreen) | ✓ | – | – | ✓ |
| [`controlsFeature`](https://videojs.org/docs/framework/react/reference/api/feature-controls) | ✓ | – | – | ✓ |
| [`bufferFeature`](https://videojs.org/docs/framework/react/reference/api/feature-buffer) | ✓ | ✓ | ✓ | ✓ |
| [`textTrackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-text-tracks) | ✓ | – | – | ✓ |
| [`playbackRateFeature`](https://videojs.org/docs/framework/react/reference/api/feature-playback-rate) | ✓ | ✓ | – | – |
| [`pipFeature`](https://videojs.org/docs/framework/react/reference/api/feature-pip) | ✓ | – | – | ✓ |
| [`errorFeature`](https://videojs.org/docs/framework/react/reference/api/feature-error) | ✓ | ✓ | ✓ | ✓ |
| [`metadataFeature`](https://videojs.org/docs/framework/react/reference/api/feature-metadata) | ✓ | ✓ | ✓ | ✓ |
| [`sourceFeature`](https://videojs.org/docs/framework/react/reference/api/feature-source) | ✓ | ✓ | ✓ | ✓ |
| [`qualityFeature`](https://videojs.org/docs/framework/react/reference/api/feature-quality) | ✓ | – | – | – |
| [`audioTrackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-audio-track) | ✓ | – | – | – |
| [`remotePlaybackFeature`](https://videojs.org/docs/framework/react/reference/api/feature-remote-playback) | ✓ | – | – | ✓ |
| [`liveFeature`](https://videojs.org/docs/framework/react/reference/api/feature-live) | – | – | ✓ | ✓ |
| [`streamTypeFeature`](https://videojs.org/docs/framework/react/reference/api/feature-stream-type) | – | – | – | – |
| [`orientationLockFeature`](https://videojs.org/docs/framework/react/reference/api/feature-orientation-lock) | – | – | – | – |
---
# createSelector
Create a type-safe selector for a store slice's state
## Import
```ts
import { createSelector } from "@videojs/store";
```
`createSelector` is also re-exported from `@videojs/react` and `@videojs/html`.
`createSelector` creates a type-safe selector function for a given slice. The returned selector extracts that slice’s state from the full store state, or returns `undefined` if the slice is not configured.
The built-in selectors ([`selectPlayback`](https://videojs.org/docs/framework/react/reference/api/feature-playback), [`selectBuffer`](https://videojs.org/docs/framework/react/reference/api/feature-buffer), etc.) are all created with `createSelector`. Use it to create selectors for custom slices.
**my-custom-selector.ts**
```ts
import { createSelector } from '@videojs/store';
import { myCustomSlice } from './my-custom-slice';
const selectCustom = createSelector(myCustomSlice);
// Use with usePlayer (React) or PlayerController (HTML)
const state = selectCustom(store.state);
```
Pass selectors to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) or [`useStore`](https://videojs.org/docs/framework/react/reference/api/use-store) for reactive subscriptions.
## API Reference
`createSelector(slice): Selector | undefined>`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `slice` (required) | `S` | — | The slice to create a selector for. |
### Return Value
| Type |
| --- |
| `{ (state: object): (S extends Slice ? Simplify & Derived> : never) \| undefined; displayName?: string }` |
---
# useStore
Hook to access store state and actions with optional selector-based subscriptions
## Import
```tsx
import { useStore } from "@videojs/react";
```
`useStore` subscribes to a store instance directly. It has the same two overloads as [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) — without a selector for store access, or with a selector for reactive state. Within `Player`, `usePlayer` is usually simpler since it reads the store from context. Reach for `useStore` when you have a store instance directly or need to derive computed values from the store.
## Examples
### Store Access
Call `useStore(store)` without a selector to get the store instance back for imperative actions. The component does not subscribe to state changes.
**App.tsx**
```tsx
import { Container, createPlayer, useStore } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
const { Player, usePlayer } = createPlayer({
features: videoFeatures,
});
function SeekControls() {
const store = usePlayer();
const s = useStore(store);
return (
s.seek(0)}>
Go to start
s.seek(s.state.duration / 2)}>
Go to middle
);
}
export default function StoreAccess() {
return (
);
}
```
**App.css**
```css
.media-container {
position: relative;
}
.media-container video {
width: 100%;
}
.controls {
display: flex;
gap: 6px;
padding: 12px;
background: rgba(0, 0, 0, 0.05);
border-top: 1px solid rgba(0, 0, 0, 0.1);
}
.controls button {
padding: 4px 12px;
font-size: 0.8125rem;
color: #111827;
cursor: pointer;
background: white;
border: 1px solid #ccc;
border-radius: 6px;
}
```
### Selector Subscription
Pass a selector to derive and subscribe to computed values from the store. The component re-renders when the derived value changes, using shallow equality by default.
**App.tsx**
```tsx
import { Container, createPlayer, useStore } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';
const { Player, usePlayer } = createPlayer({
features: videoFeatures,
});
function DerivedState() {
const store = usePlayer();
const derived = useStore(store, (s) => ({
remaining: s.duration - s.currentTime,
progress: s.duration > 0 ? (s.currentTime / s.duration) * 100 : 0,
}));
return (
Remaining
{derived.remaining.toFixed(1)}s
Progress
{derived.progress.toFixed(1)}%
);
}
export default function Selector() {
return (
);
}
```
**App.css**
```css
.media-container {
position: relative;
}
.media-container video {
width: 100%;
}
.panel {
display: flex;
gap: 16px;
padding: 12px;
margin: 0;
font-size: 0.8125rem;
background: rgba(0, 0, 0, 0.05);
border-top: 1px solid rgba(0, 0, 0, 0.1);
}
.panel div {
display: flex;
gap: 8px;
}
.panel dt {
color: #6b7280;
}
.panel dd {
margin: 0;
font-variant-numeric: tabular-nums;
}
```
## API Reference
### Without Selector
`useStore(store): S`
#### Parameters
| Parameter | Type | Default |
| --- | --- | --- |
| `store` (required) | `S` | — |
#### Return Value
`S`
### With Selector
`useStore(store, selector, isEqual?): R`
Select a value from the store. Re-renders when the selected value changes (shallowEqual).
#### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `store` (required) | `S` | — | |
| `selector` (required) | `{ (state: S['state']): R; displayName?: string }` | — | Derives a value from the store state. |
| `isEqual` | `((a: R, b: R) => boolean)` | — | Custom equality function. Defaults to `shallowEqual`. |
#### Return Value
`R`
---
# useSelector
Low-level hook for subscribing to derived state with customizable equality checks
## Import
```tsx
import { useSelector } from "@videojs/react";
```
`useSelector` is a low-level hook that subscribes to an external store using React’s `useSyncExternalStore`. It accepts a `subscribe` function, a `getSnapshot` function, a `selector` to derive state, and an optional `isEqual` comparator (defaults to `shallowEqual`).
**TimeDisplay.tsx**
```tsx
import { useSelector, shallowEqual } from "@videojs/react";
function TimeDisplay({ store }) {
const time = useSelector(
(cb) => store.subscribe(cb),
() => store.state,
(state) => ({ current: state.currentTime, duration: state.duration }),
shallowEqual,
);
return (
{time.current} / {time.duration}
);
}
```
### Relationship to useStore and useSnapshot
Both `useStore` and `useSnapshot` are built on `useSelector`:
| Hook | Input | Use case |
| --- | --- | --- |
| [`useStore`](https://videojs.org/docs/framework/react/reference/api/use-store) | Store instance | Player and store access with selector |
| [`useSnapshot`](https://videojs.org/docs/framework/react/reference/api/use-snapshot) | `State` container | Subscribe to raw state changes |
| `useSelector` | Custom subscribe/snapshot | Full control over subscription plumbing |
Prefer [`useStore`](https://videojs.org/docs/framework/react/reference/api/use-store) for store-backed state and [`useSnapshot`](https://videojs.org/docs/framework/react/reference/api/use-snapshot) for `State` containers. Use `useSelector` when you need to integrate with a non-standard external source or customize the equality comparison.
### Equality comparison
The `isEqual` parameter controls when React re-renders. The default `shallowEqual` compares object properties one level deep – sufficient for most selector return values. Pass a custom comparator for deeply nested objects or when you need reference equality (`Object.is`).
## API Reference
`useSelector(subscribe, getSnapshot, selector, isEqual?): R`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `subscribe` (required) | `((cb: (() => void)) => (() => void))` | — | Subscribe function that returns an unsubscribe callback. |
| `getSnapshot` (required) | `(() => S)` | — | Returns the current snapshot value. |
| `selector` (required) | `{ (state: S): R; displayName?: string }` | — | Derives a value from the snapshot. |
| `isEqual` | `((a: R, b: R) => boolean)` | `shallowEqual` | Custom equality function. Defaults to `shallowEqual`. |
### Return Value
`R`
---
# useSnapshot
Hook to subscribe to a State container's current value
## Import
```tsx
import { useSnapshot } from "@videojs/store/react";
```
`useSnapshot` subscribes to a `State` container and returns its current value, re-rendering when the value changes. It has two overloads:
**Full state** – returns the entire state object.
```tsx
function Display({ state }) {
const value = useSnapshot(state);
return {value.count} ;
}
```
**With selector** – returns a derived value from the state, re-rendering only when the selected value changes. Pass a custom comparator as the third argument when needed.
```tsx
function Count({ state }) {
const count = useSnapshot(state, (s) => s.count);
return {count} ;
}
```
### State containers vs stores
A `State` container is a reactive primitive that holds an object value and notifies subscribers on change. Stores are built on top of `State` containers but add features, actions, and lifecycle.
| Hook | Input | Subscribes to |
| --- | --- | --- |
| `useSnapshot` | `State` | Raw state container |
| [`useStore`](https://videojs.org/docs/framework/react/reference/api/use-store) | Store instance | Store-backed state with features |
Use `useSnapshot` when working with standalone `State` containers outside the player store system – for example, custom state in component libraries. For player state, use [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) or [`useStore`](https://videojs.org/docs/framework/react/reference/api/use-store).
`useSnapshot` is built on [`useSelector`](https://videojs.org/docs/framework/react/reference/api/use-selector) and uses `shallowEqual` by default. The HTML equivalent is `SnapshotController`.
## API Reference
### Without Selector
`useSnapshot(state): T`
#### Parameters
| Parameter | Type | Default |
| --- | --- | --- |
| `state` (required) | `{ current: Readonly; subscribe(callback: (() => void), options?: SubscribeOptions): (() => void) }` | — |
#### Return Value
`T`
### With Selector
`useSnapshot(state, selector, isEqual?): R`
Select a value from state. Re-renders when the selected value changes.
#### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `state` (required) | `{ current: Readonly; subscribe(callback: (() => void), options?: SubscribeOptions): (() => void) }` | — | |
| `selector` (required) | `{ (state: T): R; displayName?: string }` | — | Derives a value from state. |
| `isEqual` | `((a: R, b: R) => boolean)` | — | Custom equality function. Defaults to `shallowEqual`. |
#### Return Value
`R`
---
# Buffer
Buffered and seekable time range state for the player store
Tracks buffered and seekable time ranges. Each range is a `[start, end]` pair rather than a browser `TimeRanges` object.
During live playback, `seekable` tells you which times remain available. The first start is the oldest available time, and the last end is the newest. Both values can move forward as a sliding live stream drops older video and adds new video. See [live state](https://videojs.org/docs/framework/react/reference/api/feature-live) for the live-edge values.
## Import
```tsx
import { bufferFeature } from '@videojs/react';
```
The `audioFeatures`, `liveAudioFeatures`, `liveVideoFeatures`, and `videoFeatures` [feature bundles](https://videojs.org/docs/framework/react/guides/presets) include this feature.
## API Reference
### State
| Property | Type | Description |
| --- | --- | --- |
| `buffered` | `[number, number][]` | Buffered time ranges as \[start, end\] tuples. |
| `seekable` | `[number, number][]` | Seekable time ranges as \[start, end\] tuples. |
### Selector
Pass `selectBuffer` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to buffer state. Returns `undefined` if the buffer feature is not configured.
**BufferedRanges.tsx**
```tsx
import { selectBuffer, usePlayer } from '@videojs/react';
function BufferedRanges() {
const buffer = usePlayer(selectBuffer);
if (!buffer) return null;
return (
{buffer.buffered.map(([start, end]) => (
{start.toFixed(1)}–{end.toFixed(1)} seconds
))}
);
}
```
---
# Controls
User activity and controls visibility state for the player store
Tracks user activity for showing and hiding controls, with actions to toggle visibility and keep controls shown.
## Import
```tsx
import { controlsFeature } from '@videojs/react';
```
The `liveVideoFeatures` and `videoFeatures` [feature bundles](https://videojs.org/docs/framework/react/guides/presets) include this feature.
## API Reference
### State
| Property | Type | Description |
| --- | --- | --- |
| `userActive` | `boolean` | Whether the user has recently interacted with the player. |
| `controlsVisible` | `boolean` | Whether controls should be visible. |
### Actions
| Action | Type | Description |
| --- | --- | --- |
| `requestControlsLock` | `() => (() => void)` | Keep controls visible during a sustained interaction. The returned function releases the lock. Multiple concurrent locks are supported and each release function is idempotent. |
| `toggleControls` | `(forceShow?: boolean) => boolean` | Toggle controls visibility, or force it with `forceShow`. Returns the new `controlsVisible` value. |
### Selector
Pass `selectControls` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to controls state. Returns `undefined` if the controls feature is not configured.
**ControlsOverlay.tsx**
```tsx
import { selectControls, usePlayer } from '@videojs/react';
function ControlsOverlay({ children }: { children: React.ReactNode }) {
const controls = usePlayer(selectControls);
if (!controls) return null;
return (
{children}
);
}
```
---
# Error
Media error state and actions for the player store
Tracks media errors.
## Import
```tsx
import { errorFeature } from '@videojs/react';
```
The `audioFeatures`, `liveAudioFeatures`, `liveVideoFeatures`, and `videoFeatures` [feature bundles](https://videojs.org/docs/framework/react/guides/presets) include this feature.
## API Reference
### State
| Property | Type | Description |
| --- | --- | --- |
| `error` | `{ code: number; message: string } \| null` | The current media error, or null if none. |
### Actions
| Action | Type | Description |
| --- | --- | --- |
| `dismissError` | `() => void` | Dismiss the current error by clearing it. |
### Selector
Pass `selectError` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to error state. Returns `undefined` if the error feature is not configured.
**ErrorDisplay.tsx**
```tsx
import { selectError, usePlayer } from '@videojs/react';
function ErrorDisplay() {
const err = usePlayer(selectError);
if (!err?.error) return null;
return (
{err.error.message}
Dismiss
);
}
```
---
# Fullscreen
Fullscreen state and actions for the player store
Controls fullscreen mode. Tries the container element first, falls back to the media component.
## Import
```tsx
import { fullscreenFeature } from '@videojs/react';
```
The `liveVideoFeatures` and `videoFeatures` [feature bundles](https://videojs.org/docs/framework/react/guides/presets) include this feature.
## API Reference
### State
| Property | Type | Description |
| --- | --- | --- |
| `isFullscreen` | `boolean` | Whether fullscreen mode is currently active. |
| `fullscreenAvailability` | `'available' \| 'unavailable' \| 'unsupported'` | Whether fullscreen can be requested on this platform. |
### Actions
| Action | Type | Description |
| --- | --- | --- |
| `requestFullscreen` | `() => Promise` | Enter fullscreen mode. Tries container first, falls back to media component. |
| `exitFullscreen` | `() => Promise` | Exit fullscreen mode. |
### Selector
Pass `selectFullscreen` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to fullscreen state. Returns `undefined` if the fullscreen feature is not configured.
**FullscreenButton.tsx**
```tsx
import { selectFullscreen, usePlayer } from '@videojs/react';
function FullscreenButton() {
const fs = usePlayer(selectFullscreen);
if (!fs || fs.fullscreenAvailability !== 'available') return null;
return (
(fs.isFullscreen ? fs.exitFullscreen() : fs.requestFullscreen())}>
{fs.isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}
);
}
```
### Screen orientation
Add [`features.orientationLock`](https://videojs.org/docs/framework/react/reference/api/feature-orientation-lock) to lock screen orientation while fullscreen is active. Unsupported browsers and rejected lock requests are ignored, so iOS Safari continues to use its normal fullscreen behavior.
The feature locks to `landscape` unless the provider sets another Screen Orientation API type.
```tsx
```
See [Orientation lock](https://videojs.org/docs/framework/react/reference/api/feature-orientation-lock) for the player setup.
---
# Live
Live edge state for the player store
Tracks when playback is at the live edge and what kind of live window the stream provides.
- `liveEdgeStart` is the playback time where the live edge begins. Playback counts as live when `currentTime` reaches this value.
- `targetLiveWindow` describes the kind of live window. Despite its name, it does not report a number of seconds: `0` means a sliding live window, `Infinity` means a live event with playback history, and `NaN` means on-demand or not known yet.
Both values are `NaN` when the media does not provide live-edge information.
Use the [buffer feature](https://videojs.org/docs/framework/react/reference/api/feature-buffer) to find the times a viewer can seek to. Its `seekable` value contains `[start, end]` pairs. On a sliding live stream, the oldest and newest available times both move forward.
The live presets do not include the [stream type feature](https://videojs.org/docs/framework/react/reference/api/feature-stream-type), so `selectStreamType` returns `undefined` unless you add it to a custom player.
The [time feature](https://videojs.org/docs/framework/react/reference/api/feature-time) reports the end of the available live video as `duration`, even when the browser reports `Infinity`. To check for live playback, use `!Number.isNaN(targetLiveWindow)` instead of checking `duration`.
## Import
```tsx
import { liveFeature } from '@videojs/react';
```
The `liveAudioFeatures` and `liveVideoFeatures` [feature bundles](https://videojs.org/docs/framework/react/guides/presets) include this feature.
## API Reference
### State
| Property | Type | Description |
| --- | --- | --- |
| `liveEdgeStart` | `number` | Playback time where the live edge begins. Playback is live when `currentTime >= liveEdgeStart`. `NaN` when the stream is not live or the value is unknown. |
| `targetLiveWindow` | `number` | Describes the kind of live window available. This value is not a duration. `0` for a sliding live window, `Infinity` for a live event with playback history, and `NaN` for on-demand or unknown. |
### Selector
Pass `selectLive` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to live state. Returns `undefined` if the live feature is not configured.
**LiveEdgeIndicator.tsx**
```tsx
import { selectLive, selectTime, usePlayer } from '@videojs/react';
function LiveEdgeIndicator() {
const live = usePlayer(selectLive);
const time = usePlayer(selectTime);
if (!live || Number.isNaN(live.targetLiveWindow)) return null;
const atEdge = time != null && time.currentTime >= live.liveEdgeStart;
return {atEdge ? 'LIVE' : 'BEHIND LIVE'} ;
}
```
## Jump to the live edge
Use the packaged live button for the usual jump-to-live behavior. It combines the live, time, and buffer features, then seeks to the end of the last `seekable` range. It also manages its accessible label and disabled state.
**StationLiveButton.tsx**
```tsx
import { LiveButton } from '@videojs/react';
export function StationLiveButton() {
return On air ;
}
```
Put the button in your custom controls and style it there. Do not calculate the destination from `duration` or set `currentTime` to `Infinity`.
If you need different behavior, you can combine the same public features yourself:
- Read `targetLiveWindow` from `selectLive` to check that the source is live.
- Read the newest available time from the last range returned by `selectBuffer`.
- Call the `seek()` action returned by `selectTime` with that time.
**CustomLiveButton.tsx**
```tsx
import { selectBuffer, selectLive, selectTime, usePlayer } from '@videojs/react';
export function CustomLiveButton() {
const live = usePlayer(selectLive);
const time = usePlayer(selectTime);
const buffer = usePlayer(selectBuffer);
const newestTime = buffer?.seekable.at(-1)?.[1];
const jumpToLive = () => {
if (!time || newestTime === undefined || !Number.isFinite(newestTime)) return;
void time.seek(newestTime);
};
const disabled = !live || Number.isNaN(live.targetLiveWindow) || !Number.isFinite(newestTime);
return (
Go live
);
}
```
---
# Metadata
Resolved title and poster values for the player store
Resolves what is playing into two values your UI can render: `title` and `poster`.
`title` and `poster` each resolve independently through the same two tiers: the value you set, then the value the media reports. The first tier that holds a value wins; when neither does, the resolved value is an empty string. Because the tiers stay separate, clearing the value you set reveals the media’s, and a source that reports a poster but no title contributes to one while leaving the other empty.
An empty string counts as a value and stops the chain: set `title` to `''` and the resolved title is `''`. Pass `null` to clear your value.
The metadata feature is included by the `videoFeatures`, `audioFeatures`, `liveVideoFeatures`, and `liveAudioFeatures` [presets](https://videojs.org/docs/framework/react/guides/presets); apps that build a custom preset can compose it in directly.
## Import
```tsx
import { metadataFeature } from '@videojs/react';
```
The `audioFeatures`, `liveAudioFeatures`, `liveVideoFeatures`, and `videoFeatures` [feature bundles](https://videojs.org/docs/framework/react/guides/presets) include this feature.
## API Reference
### Configuration
Props the Player component accepts. They exist only while this feature is selected.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `title` | `undefined \| null \| string` | — | The title to display. Takes precedence over the title the media carries. |
| `poster` | `undefined \| null \| string` | — | The poster to display. Takes precedence over the poster the media carries. |
### State
| Property | Type | Description |
| --- | --- | --- |
| `title` | `string` | The resolved content title. Set it through the player, not through the store. |
| `poster` | `string` | The resolved poster URL, independent of the media component's own `poster`. Set it through the player, not through the store. |
### Selector
To just show the name of what is playing, drop in the [Title component](https://videojs.org/docs/framework/react/reference/components/title) — it reads this feature for you. Reach for the selector when building your own UI.
Pass `selectMetadata` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to metadata state. Returns `undefined` if the metadata feature is not configured.
**ContentTitle.tsx**
```tsx
import { selectMetadata, usePlayer } from '@videojs/react';
function ContentTitle() {
const metadata = usePlayer(selectMetadata);
if (!metadata?.title) return null;
return {metadata.title} ;
}
```
### Player inputs
The store publishes the resolved values and takes no writes. A player input is the only way to set one.
Every input is a prop on [`Player`](https://videojs.org/docs/framework/react/reference/components/player). A player built without the metadata feature has none of them, and none is forwarded.
**App.tsx**
```tsx
import { Container, createPlayer } from '@videojs/react';
import { videoFeatures } from '@videojs/react/video';
const { Player } = createPlayer({ features: videoFeatures });
function App({ episode }: { episode: { title: string | null; art: string | null } }) {
return (
);
}
```
Passing `null` clears your value, so `title={null}` falls through to the title the media reports.
### Media-reported values
The media tier comes from media that reports content data and announces changes to it, and it covers `title` and `poster`. A media reports only the keys it can vouch for, so either may be absent while the other arrives. A key that never arrives means that tier never contributes, and the resolved value is whatever you set. Detaching the media clears its values; the values you set survive and apply to the next source.
`poster` is the feature’s own resolved value, not the media component’s `poster` attribute. Setting one does not set the other.
A low-resolution stand-in to show while the poster loads is not part of this feature. The [poster](https://videojs.org/docs/framework/react/reference/components/poster) renders an ` ` you control, so give it a `background-image` and that shows until the poster itself paints over it. [Add a poster and loading placeholder](https://videojs.org/docs/framework/react/guides/poster) walks through that technique for each framework.
---
# Orientation lock
Screen orientation locking while fullscreen is active
Locks screen orientation while fullscreen is active.
## Import
```tsx
import { orientationLockFeature } from '@videojs/react';
```
No packaged [feature bundle](https://videojs.org/docs/framework/react/guides/presets) includes this feature — add it to your [`createPlayer`](https://videojs.org/docs/framework/react/reference/api/create-player) features yourself.
## API Reference
### Configuration
Props the Player component accepts. They exist only while this feature is selected.
| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `orientationLockType` | `undefined \| null \| 'any' \| 'landscape' \| 'landscape-primary' \| 'landscape-secondary' \| 'natural' \| 'portrait' \| 'portrait-primary' \| 'portrait-secondary'` | `'landscape'` | Screen orientation type to lock while fullscreen is active. |
### State
| Property | Type | Description |
| --- | --- | --- |
| `orientationLockType` | `'any' \| 'landscape' \| 'landscape-primary' \| 'landscape-secondary' \| 'natural' \| 'portrait' \| 'portrait-primary' \| 'portrait-secondary'` | Screen orientation type locked while fullscreen is active. |
### Actions
| Action | Type | Description |
| --- | --- | --- |
| `setOrientationLockType` | `(value: 'any' \| 'landscape' \| 'landscape-primary' \| 'landscape-secondary' \| 'natural' \| 'portrait' \| 'portrait-primary' \| 'portrait-secondary' \| null \| undefined) => void` | Sets the locked orientation type. A missing value, including an empty one, restores the default. |
### Usage
Selecting the feature adds it to the player.
**player.tsx**
```tsx
import { Container, createPlayer, features } from '@videojs/react';
import { videoFeatures } from '@videojs/react/video';
export const { Player } = createPlayer({
features: [...videoFeatures, features.orientationLock],
});
```
### Orientation type
The feature locks to `landscape` unless the provider sets another Screen Orientation API type. The value can change while the player is running; if the screen is already locked, it re-locks to the new type.
```tsx
```
Clearing the value restores `landscape`.
### Selector
Pass `selectOrientationLock` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to the lock state. Returns `undefined` if the orientation lock feature is not configured.
**OrientationToggle.tsx**
```tsx
import { selectOrientationLock, usePlayer } from '@videojs/react';
function OrientationToggle() {
const lock = usePlayer(selectOrientationLock);
if (!lock) return null;
return (
lock.setOrientationLockType('portrait')}>
Lock portrait
);
}
```
Unsupported browsers and rejected lock requests are ignored.
---
# Picture-in-picture
Picture-in-picture state and actions for the player store
Controls picture-in-picture mode.
## Import
```tsx
import { pipFeature } from '@videojs/react';
```
The `liveVideoFeatures` and `videoFeatures` [feature bundles](https://videojs.org/docs/framework/react/guides/presets) include this feature.
## API Reference
### State
| Property | Type | Description |
| --- | --- | --- |
| `isPictureInPicture` | `boolean` | Whether picture-in-picture mode is currently active. |
| `pictureInPictureAvailability` | `'available' \| 'unavailable' \| 'unsupported'` | Whether picture-in-picture can be requested on this platform. |
### Actions
| Action | Type | Description |
| --- | --- | --- |
| `requestPictureInPicture` | `() => Promise` | Enter picture-in-picture mode, exiting fullscreen first. Rejects before metadata is loaded. |
| `exitPictureInPicture` | `() => Promise` | Exit picture-in-picture mode. |
### Selector
Pass `selectPiP` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to picture-in-picture state. Returns `undefined` if the PiP feature is not configured.
**PiPButton.tsx**
```tsx
import { selectPiP, usePlayer } from '@videojs/react';
function PiPButton() {
const pip = usePlayer(selectPiP);
if (!pip || pip.pictureInPictureAvailability !== 'available') return null;
return (
(pip.isPictureInPicture ? pip.exitPictureInPicture() : pip.requestPictureInPicture())}>
{pip.isPictureInPicture ? 'Exit PiP' : 'Picture-in-Picture'}
);
}
```
---
# Playback
Play/pause state and actions for the player store
Controls play/pause state and tracks whether playback has started or is stalled.
## Import
```tsx
import { playbackFeature } from '@videojs/react';
```
The `audioFeatures`, `liveAudioFeatures`, `liveVideoFeatures`, and `videoFeatures` [feature bundles](https://videojs.org/docs/framework/react/guides/presets) include this feature.
## API Reference
### State
| Property | Type | Description |
| --- | --- | --- |
| `paused` | `boolean` | Whether playback is paused. |
| `ended` | `boolean` | Whether playback has reached the end. |
| `started` | `boolean` | Whether playback has started (played or seeked). |
| `waiting` | `boolean` | Whether playback is stalled waiting for data. |
### Actions
| Action | Type | Description |
| --- | --- | --- |
| `play` | `() => Promise` | Start playback. Updates `paused` immediately when the media starts. |
| `pause` | `() => void` | Pause playback. Updates `paused` immediately. |
### Selector
Pass `selectPlayback` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to playback state. Returns `undefined` if the playback feature is not configured.
**PlayButton.tsx**
```tsx
import { selectPlayback, usePlayer } from '@videojs/react';
function PlayButton() {
const playback = usePlayer(selectPlayback);
if (!playback) return null;
const toggle = () => (playback.paused ? playback.play() : playback.pause());
return {playback.paused ? 'Play' : 'Pause'} ;
}
```
---
# Playback rate
Playback speed state and actions for the player store
Controls speed of playback.
## Import
```tsx
import { playbackRateFeature } from '@videojs/react';
```
The `audioFeatures` and `videoFeatures` [feature bundles](https://videojs.org/docs/framework/react/guides/presets) include this feature.
## API Reference
### State
| Property | Type | Description |
| --- | --- | --- |
| `playbackRates` | `readonly number[]` | Available playback rates. |
| `playbackRate` | `number` | Current playback rate. |
### Actions
| Action | Type | Description |
| --- | --- | --- |
| `setPlaybackRate` | `(rate: number) => void` | Set the playback rate. |
### Selector
Pass `selectPlaybackRate` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to playback rate state. Returns `undefined` if the playback rate feature is not configured.
**RateDisplay.tsx**
```tsx
import { selectPlaybackRate, usePlayer } from '@videojs/react';
function RateDisplay() {
const rate = usePlayer(selectPlaybackRate);
if (!rate) return null;
return {rate.playbackRate}x ;
}
```
---
# Quality
Video rendition state and actions for the player store
Tracks video renditions and selects a manual rendition or automatic adaptive bitrate.
## Import
```tsx
import { qualityFeature } from '@videojs/react';
```
The `videoFeatures` [feature bundle](https://videojs.org/docs/framework/react/guides/presets) includes this feature.
## API Reference
### State
| Property | Type | Description |
| --- | --- | --- |
| `videoRenditionList` | `MediaVideoRendition[]` | Video renditions available for manual quality selection. |
| `activeVideoRendition` | `{ id: string; width?: number; height?: number; bitrate?: number; frameRate?: number; codec?: string; selected: boolean } \| null` | Video rendition currently playing, including when automatic ABR is selected. |
### Actions
| Action | Type | Description |
| --- | --- | --- |
| `selectVideoRendition` | `(id: string) => void` | Select a video rendition by `id`, or automatic ABR with `"auto"`. |
### Selector
Pass `selectQuality` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to quality state. Returns `undefined` if the quality feature is not configured.
**QualityInfo.tsx**
```tsx
import { selectQuality, usePlayer } from '@videojs/react';
function QualityInfo() {
const quality = usePlayer(selectQuality);
if (!quality) return null;
return {quality.videoRenditionList.length} renditions ;
}
```
---
# Audio track
Audio track state and actions for the player store
Tracks available audio tracks and selects the enabled track.
## Import
```tsx
import { audioTrackFeature } from '@videojs/react';
```
The `videoFeatures` [feature bundle](https://videojs.org/docs/framework/react/guides/presets) includes this feature.
## API Reference
### State
| Property | Type | Description |
| --- | --- | --- |
| `audioTrackList` | `MediaAudioTrack[]` | Audio tracks available for manual track selection. |
### Actions
| Action | Type | Description |
| --- | --- | --- |
| `selectAudioTrack` | `(id: string) => void` | Select an audio track by `id`. |
### Selector
Pass `selectAudioTrack` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to audio track state. Returns `undefined` if the audio track feature is not configured.
**AudioTrackInfo.tsx**
```tsx
import { selectAudioTrack, usePlayer } from '@videojs/react';
function AudioTrackInfo() {
const audioTrack = usePlayer(selectAudioTrack);
if (!audioTrack) return null;
return {audioTrack.audioTrackList.length} audio tracks ;
}
```
---
# Remote Playback
Remote playback state and actions for the player store
Controls remote playback to devices like Chromecast (Chromium) and AirPlay (Safari). Exits fullscreen before initiating a remote playback session.
- [Cast to AirPlay and Chromecast](https://videojs.org/docs/framework/react/guides/casting)
## Import
```tsx
import { remotePlaybackFeature } from '@videojs/react';
```
The `liveVideoFeatures` and `videoFeatures` [feature bundles](https://videojs.org/docs/framework/react/guides/presets) include this feature.
## API Reference
### State
| Property | Type | Description |
| --- | --- | --- |
| `remotePlaybackState` | `'disconnected' \| 'connecting' \| 'connected'` | Current remote playback connection state. |
| `remotePlaybackAvailability` | `'available' \| 'unavailable' \| 'unsupported'` | Whether remote playback can be requested on this platform. |
### Actions
| Action | Type | Description |
| --- | --- | --- |
| `promptRemotePlayback` | `() => Promise` | Prompt the user to pick a remote playback device. Exits fullscreen first when connecting. |
### Selector
Pass `selectRemotePlayback` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to remote playback state. Returns `undefined` if the remote playback feature is not configured.
**CastButton.tsx**
```tsx
import { selectRemotePlayback, usePlayer } from '@videojs/react';
function CastButton() {
const remotePlayback = usePlayer(selectRemotePlayback);
if (!remotePlayback || remotePlayback.remotePlaybackAvailability !== 'available') return null;
return (
{remotePlayback.remotePlaybackState === 'connected' ? 'Disconnect' : 'Cast'}
);
}
```
---
# Source
Media source state and actions for the player store
Tracks the loaded media resource and how much of it is ready. To change the source, set `src` or `source` on the media component, which also carries type and engine configuration.
## Import
```tsx
import { sourceFeature } from '@videojs/react';
```
The `audioFeatures`, `liveAudioFeatures`, `liveVideoFeatures`, and `videoFeatures` [feature bundles](https://videojs.org/docs/framework/react/guides/presets) include this feature.
## API Reference
### State
| Property | Type | Description |
| --- | --- | --- |
| `currentSrc` | `string` | Current media source URL (empty string if none). |
| `canPlay` | `boolean` | Whether enough data is loaded to begin playback. |
### Selector
Pass `selectSource` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to source state. Returns `undefined` if the source feature is not configured.
**SourceInfo.tsx**
```tsx
import { selectSource, usePlayer } from '@videojs/react';
function SourceInfo() {
const source = usePlayer(selectSource);
if (!source) return null;
return {source.currentSrc || 'No source'} ;
}
```
---
# Stream type
Stream delivery type (live / on-demand) state for the player store
Tracks whether the current source is live, on-demand, or not known yet. Use it to choose which controls to show.
`streamType` is `'live'`, `'on-demand'`, or `'unknown'`. It does not tell you how close playback is to the live edge or whether viewers can rewind.
HLS media reads the stream type from the loaded playlist. If you set `streamType` yourself, your value takes priority until you set it back to `'unknown'`. Other media falls back to the browser’s duration: `Infinity` means live, a finite positive value means on-demand, and any other value means unknown.
Add `streamTypeFeature` to a custom player when you need `streamType` in player state. The live presets separately include the [live feature](https://videojs.org/docs/framework/react/reference/api/feature-live), which tracks the live edge.
## Import
```tsx
import { streamTypeFeature } from '@videojs/react';
```
No packaged [feature bundle](https://videojs.org/docs/framework/react/guides/presets) includes this feature — add it to your [`createPlayer`](https://videojs.org/docs/framework/react/reference/api/create-player) features yourself.
## API Reference
### State
| Property | Type | Description |
| --- | --- | --- |
| `streamType` | `'on-demand' \| 'live' \| 'unknown'` | Current stream delivery type. Components use this to show live-specific UI (for example, a live indicator or a "jump to live edge" button) or hide the time display. |
### Selector
Pass `selectStreamType` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to stream type state. Returns `undefined` if the stream type feature is not configured.
**LiveIndicator.tsx**
```tsx
import { createPlayer, selectStreamType, streamTypeFeature } from '@videojs/react';
import { liveVideoFeatures } from '@videojs/react/live-video';
const { Player, usePlayer } = createPlayer({
features: [...liveVideoFeatures, streamTypeFeature],
});
function LiveIndicator() {
const stream = usePlayer(selectStreamType);
if (stream?.streamType !== 'live') return null;
return LIVE ;
}
```
---
# Text tracks
Subtitles, captions, and chapter track state for the player store
Manages subtitles, captions, chapters, and thumbnail tracks.
A default `kind="chapters"` track can also divide the [time slider](https://videojs.org/docs/framework/react/reference/components/time-slider) into chapter ranges and label the chapter at the current interaction position.
`thumbnailsTrack` is the first `kind="metadata"` track labeled `thumbnails`, or `null` when there is none. Its `crossOrigin` reports the media component’s CORS mode, or `null` when the media is not CORS-enabled. [Thumbnail](https://videojs.org/docs/framework/react/reference/components/thumbnail) reads it to fetch sprite sheets the same way the browser fetched the track.
## Import
```tsx
import { textTrackFeature } from '@videojs/react';
```
The `liveVideoFeatures` and `videoFeatures` [feature bundles](https://videojs.org/docs/framework/react/guides/presets) include this feature.
## API Reference
### State
| Property | Type | Description |
| --- | --- | --- |
| `textTrackList` | `MediaTextTrack[]` | All text tracks available on the media component. |
| `subtitlesShowing` | `boolean` | Whether a captions/subtitles track is showing. |
| `chaptersCues` | `MediaTextCue[]` | Cues from the first `kind="chapters"` track, with cue ends clamped to a finite media duration. |
| `thumbnailsTrack` | `{ cues: MediaTextCue[]; src: string \| null; crossOrigin: 'anonymous' \| 'use-credentials' \| null } \| null` | The first thumbnails track, or `null` when there is none. |
### Actions
| Action | Type | Description |
| --- | --- | --- |
| `toggleSubtitles` | `(forceShow?: boolean) => boolean` | Toggle captions/subtitles visibility. Showing enables one caption/subtitle track. A track already showing stays selected. Otherwise, selection prefers the last track shown, then a track matching the browser language, then the first available track. Returns whether a track is showing. |
| `selectSubtitlesTrack` | `(id: string \| null) => void` | Show the captions/subtitles track with `id`, or turn captions/subtitles off with `null`. |
### Selector
Pass `selectTextTrack` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to text track state. Returns `undefined` if the text tracks feature is not configured.
**CaptionsButton.tsx**
```tsx
import { selectTextTrack, usePlayer } from '@videojs/react';
function CaptionsButton() {
const tracks = usePlayer(selectTextTrack);
if (!tracks) return null;
return (
tracks.toggleSubtitles()}>
{tracks.subtitlesShowing ? 'Hide captions' : 'Show captions'}
);
}
```
---
# Time
Playback position and duration state for the player store
Tracks playback position and duration. During live playback, the browser may report `Infinity` as the duration. The player reports the end of the last available [seekable range](https://videojs.org/docs/framework/react/reference/api/feature-buffer) instead, so the value stays finite and moves forward with the stream. This is the newest available time, not the length of the rewindable window; its first available time may be greater than zero. Use [live state](https://videojs.org/docs/framework/react/reference/api/feature-live) or [stream type](https://videojs.org/docs/framework/react/reference/api/feature-stream-type) to check whether the source is live.
## Import
```tsx
import { timeFeature } from '@videojs/react';
```
The `audioFeatures`, `liveAudioFeatures`, `liveVideoFeatures`, and `videoFeatures` [feature bundles](https://videojs.org/docs/framework/react/guides/presets) include this feature.
## API Reference
### State
| Property | Type | Description |
| --- | --- | --- |
| `currentTime` | `number` | Current playback position in seconds. |
| `duration` | `number` | Total duration in seconds (0 if unknown). |
| `seeking` | `boolean` | Whether a seek operation is in progress. |
### Actions
| Action | Type | Description |
| --- | --- | --- |
| `seek` | `(time: number) => Promise` | Seek to a time in seconds. Returns the actual position after seek. |
### Selector
Pass `selectTime` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to time state. Returns `undefined` if the time feature is not configured.
**TimeDisplay.tsx**
```tsx
import { selectTime, usePlayer } from '@videojs/react';
function TimeDisplay() {
const time = usePlayer(selectTime);
if (!time) return null;
return (
{Math.floor(time.currentTime)} / {Math.floor(time.duration)}
);
}
```
---
# Volume
Volume level and mute state for the player store
Controls volume level and mute state. These capabilities have independent availability values because media can support mute without supporting volume-level changes.
## Import
```tsx
import { volumeFeature } from '@videojs/react';
```
The `audioFeatures`, `liveAudioFeatures`, `liveVideoFeatures`, and `videoFeatures` [feature bundles](https://videojs.org/docs/framework/react/guides/presets) include this feature.
## API Reference
### State
| Property | Type | Description |
| --- | --- | --- |
| `volume` | `number` | Volume level from 0 (silent) to 1 (max). |
| `muted` | `boolean` | Whether audio is muted. |
| `volumeAvailability` | `'available' \| 'unavailable' \| 'unsupported'` | Whether volume can be programmatically set on this platform. |
| `mutedAvailability` | `'available' \| 'unavailable' \| 'unsupported'` | Whether the media can be muted. Separate from `volumeAvailability` because the two come apart: an embed can take a mute command while offering no way to set a level, and iOS Safari refuses a volume write on media that mutes perfectly well. |
### Actions
| Action | Type | Description |
| --- | --- | --- |
| `setVolume` | `(volume: number) => number` | Set volume (clamped 0-1). Returns the clamped value. |
| `setMuted` | `(muted: boolean) => boolean` | Set the muted state, updating the store immediately. Unmuting at volume 0 restores volume to 0.25. Returns the new muted value. |
### Selector
Pass `selectVolume` to [`usePlayer`](https://videojs.org/docs/framework/react/reference/api/use-player) to subscribe to volume state. Returns `undefined` if the volume feature is not configured.
Check `volumeAvailability` before showing a volume-level control, and check `mutedAvailability` before showing a mute control.
**VolumeSlider.tsx**
```tsx
import { selectVolume, usePlayer } from '@videojs/react';
function VolumeSlider() {
const vol = usePlayer(selectVolume);
if (!vol || vol.volumeAvailability !== 'available') return null;
return (
vol.setVolume(Number(e.target.value))}
/>
);
}
```
---
# useAudioTrackOptions
Hook to build audio track menu options from the player audio track state
## Import
```tsx
import { useAudioTrackOptions } from "@videojs/react";
```
`useAudioTrackOptions` returns the currently enabled audio track, the available options, and a `setValue` callback for wiring track selection into a menu. It returns `null` when the [audio track feature](https://videojs.org/docs/framework/react/reference/api/feature-audio-track) is not configured.
**AudioMenu.tsx**
```tsx
import { Menu, useAudioTrackOptions } from "@videojs/react";
function AudioMenu() {
const audioTrack = useAudioTrackOptions();
if (audioTrack?.state.availability !== "available") return null;
return (
{audioTrack.options.map((option) => (
{option.label}
))}
);
}
```
Each `AudioTrackOption` has a `value`, a translated `label`, and a `disabled` flag. Option labels use the track label, then language, then kind. Pass `formatTrack` to customize the visible labels.
See [AudioTrackRadioGroup](https://videojs.org/docs/framework/react/reference/components/audio-track-radio-group) for the component-level pattern, and [Menu](https://videojs.org/docs/framework/react/reference/components/menu) for a complete settings menu example.
## API Reference
`useAudioTrackOptions(props?): AudioTrackOptionsResult | null`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `props` | `{ label?: Text \| string \| ((state: AudioTrackRadioGroupState) => Text \| string); formatTrack?: ((track: MediaAudioTrack) => Text \| string); disabled?: boolean }` | — | Optional `label`, `formatTrack`, and `disabled` overrides. |
### Return Value
| Property | Type |
| --- | --- |
| `state` | `{ label: Text \| string; value: string; options: readonly Option[]; disabled: boolean; hidden: boolean; availability: 'available' \| 'unavailable' }` |
| `label` | `string` |
| `value` | `string` |
| `selectedLabel` | `string` |
| `options` | `AudioTrackOption[]` |
| `disabled` | `boolean` |
| `hidden` | `boolean` |
| `setValue` | `((value: string) => void)` |
---
# useCaptionsOptions
Hook to build captions menu options from the player text track state
## Import
```tsx
import { useCaptionsOptions } from "@videojs/react";
```
`useCaptionsOptions` returns the currently showing caption track, the available options (starting with an `Off` option), and a `setValue` callback for wiring caption selection into a menu. It returns `null` when the [text tracks feature](https://videojs.org/docs/framework/react/reference/api/feature-text-tracks) is not configured.
**CaptionsMenu.tsx**
```tsx
import { Menu, useCaptionsOptions } from "@videojs/react";
function CaptionsMenu() {
const captions = useCaptionsOptions();
if (captions?.state.availability !== "available") return null;
return (
{captions.options.map((option) => (
{option.label}
))}
);
}
```
Each `CaptionsOption` has a `value`, a translated `label`, and a `disabled` flag. Option labels use the track label, then language, then kind. Pass `formatTrack` to customize the visible labels. The result also exposes `showMenu`, which is `true` when more than one caption or subtitle track is available — the same threshold [CaptionsButton](https://videojs.org/docs/framework/react/reference/components/captions-button) uses to open a menu instead of toggling in `menuTrigger` mode.
See [CaptionsRadioGroup](https://videojs.org/docs/framework/react/reference/components/captions-radio-group) for the component-level pattern, and [Menu](https://videojs.org/docs/framework/react/reference/components/menu) for a complete settings menu example.
## API Reference
`useCaptionsOptions(props?): CaptionsOptionsResult | null`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `props` | `{ label?: Text \| string \| ((state: CaptionsRadioGroupState) => Text \| string); formatTrack?: ((track: MediaTextTrack) => Text \| string); disabled?: boolean }` | — | Optional `label`, `formatTrack`, and `disabled` overrides. |
### Return Value
| Property | Type |
| --- | --- |
| `state` | `{ subtitlesShowing: boolean; label: Text \| string; value: string; options: readonly Option[]; disabled: boolean; hidden: boolean; availability: 'available' \| 'unavailable' }` |
| `label` | `string` |
| `value` | `string` |
| `selectedLabel` | `string` |
| `options` | `CaptionsOption[]` |
| `disabled` | `boolean` |
| `hidden` | `boolean` |
| `showMenu` | `boolean` |
| `setValue` | `((value: string) => void)` |
---
# usePlaybackRateOptions
Hook to build playback rate menu options from the player playback rate state
## Import
```tsx
import { usePlaybackRateOptions } from "@videojs/react";
```
`usePlaybackRateOptions` returns the current playback rate, the available options, and `setRate` / `setValue` callbacks for wiring rate selection into a menu. It returns `null` when the [playback rate feature](https://videojs.org/docs/framework/react/reference/api/feature-playback-rate) is not configured.
**SpeedMenu.tsx**
```tsx
import { Menu, usePlaybackRateOptions } from "@videojs/react";
function SpeedMenu() {
const playbackRate = usePlaybackRateOptions();
if (playbackRate?.state.availability !== "available") return null;
return (
{playbackRate.options.map((option) => (
{option.label}
))}
);
}
```
Options come from the player’s configured playback rates. Each `PlaybackRateOption` has a string `value`, its numeric `rate`, a translated `label`, and a `disabled` flag. Labels default to the rate followed by a multiplication sign (e.g. `1.5×`); pass `formatRate` to customize them. `setValue` accepts an option’s string value, while `setRate` accepts a number directly.
See [PlaybackRateRadioGroup](https://videojs.org/docs/framework/react/reference/components/playback-rate-radio-group) for the component-level pattern, and [Menu](https://videojs.org/docs/framework/react/reference/components/menu) for a complete settings menu example.
## API Reference
`usePlaybackRateOptions(props?): PlaybackRateOptionsResult | null`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `props` | `{ label?: Text \| string \| ((state: PlaybackRateRadioGroupState) => Text \| string); formatRate?: ((rate: number) => string); disabled?: boolean }` | — | Optional `label`, `formatRate`, and `disabled` overrides. |
### Return Value
| Property | Type |
| --- | --- |
| `state` | `{ label: Text \| string; value: string; options: readonly Option[]; disabled: boolean; hidden: boolean; availability: 'available' \| 'unavailable'; rate: number }` |
| `label` | `string` |
| `rate` | `number` |
| `value` | `string` |
| `selectedLabel` | `string` |
| `options` | `PlaybackRateOption[]` |
| `disabled` | `boolean` |
| `hidden` | `boolean` |
| `setRate` | `((rate: number) => void)` |
| `setValue` | `((value: string) => void)` |
---
# useQualityOptions
Hook to build video quality menu options from the player rendition state
## Import
```tsx
import { useQualityOptions } from "@videojs/react";
```
`useQualityOptions` returns the current quality value, the available options, and a `setValue` callback for wiring quality selection into a menu. It returns `null` when the [quality feature](https://videojs.org/docs/framework/react/reference/api/feature-quality) is not configured.
**QualityMenu.tsx**
```tsx
import { Menu, useQualityOptions } from "@videojs/react";
function QualityMenu() {
const quality = useQualityOptions();
if (quality?.state.availability !== "available") return null;
return (
{quality.options.map((option) => (
{option.label}
))}
);
}
```
The first option is always `Auto`; selecting it returns control to adaptive bitrate selection. Each `QualityOption` has a `value`, a translated `label`, a `disabled` flag, and optional `tier` and `badge` fields (e.g. `4K`, `HD`) for richer labels. Pass `formatRendition` to customize the visible rendition labels.
See [QualityRadioGroup](https://videojs.org/docs/framework/react/reference/components/quality-radio-group) for the component-level pattern, and [Menu](https://videojs.org/docs/framework/react/reference/components/menu) for a complete settings menu example.
## API Reference
`useQualityOptions(props?): QualityOptionsResult | null`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `props` | `{ label?: Text \| string \| ((state: QualityRadioGroupState) => Text \| string); formatRendition?: ((rendition: MediaVideoRendition) => Text \| string); disabled?: boolean }` | — | Optional `label`, `formatRendition`, and `disabled` overrides. |
### Return Value
| Property | Type |
| --- | --- |
| `state` | `{ label: Text \| string; value: string; options: readonly Option[]; disabled: boolean; hidden: boolean; availability: 'available' \| 'unavailable' }` |
| `label` | `string` |
| `value` | `string` |
| `selectedLabel` | `string` |
| `options` | `QualityOption[]` |
| `disabled` | `boolean` |
| `hidden` | `boolean` |
| `setValue` | `((value: string) => void)` |
---
# useTapGesture
Hook that registers a tap gesture for the current Player
## Import
```tsx
import { useTapGesture } from "@videojs/react";
```
## Usage
Call the hook inside `Player`. It listens on the current player container unless `target` supplies another element.
```tsx
function TapFeedback() {
useTapGesture(() => console.log("Tap"), { region: "center" });
return null;
}
```
Use `pointer`, `region`, and `disabled` to control recognition. Supplying `target` does not make the hook safe outside `Player`, because it still reads the player container context.
## API Reference
`useTapGesture(onActivate, options?): void`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `onActivate` (required) | `((event: PointerEvent) => void)` | — | Callback invoked when a matching tap is recognized. |
| `options` | `{ pointer?: 'mouse' \| 'touch' \| 'pen'; region?: 'left' \| 'center' \| 'right'; disabled?: boolean; target?: RefObject }` | — | Gesture matching, target, and disabled options. |
### Return Value
`void`
---
# useDoubleTapGesture
Hook that registers a double-tap gesture for the current Player
## Import
```tsx
import { useDoubleTapGesture } from "@videojs/react";
```
## Usage
Call the hook inside `Player`. It listens on the current player container unless `target` supplies another element.
```tsx
function DoubleTapFeedback() {
useDoubleTapGesture(() => console.log("Double tap"), { region: "right" });
return null;
}
```
Use `pointer`, `region`, and `disabled` to control recognition. Supplying `target` does not make the hook safe outside `Player`, because it still reads the player container context.
## API Reference
`useDoubleTapGesture(onActivate, options?): void`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `onActivate` (required) | `((event: PointerEvent) => void)` | — | Callback invoked when a matching double tap is recognized. |
| `options` | `{ pointer?: 'mouse' \| 'touch' \| 'pen'; region?: 'left' \| 'center' \| 'right'; disabled?: boolean; target?: RefObject }` | — | Gesture matching, target, and disabled options. |
### Return Value
`void`
---
# useHotkey
Hook that registers a keyboard shortcut for the current Player
## Import
```tsx
import { useHotkey } from "@videojs/react";
```
## Usage
Call the hook inside `Player` with the accepted keys and activation callback.
```tsx
function CustomPlayHotkey() {
useHotkey({
keys: "k",
onActivate: (_event, key) => console.log(`Activated by ${key}`),
});
// The hook must run inside a component, but this one has no UI to render.
return null;
}
```
Shortcuts target the player by default and accept repeated keydown events, unless `action` names a toggle such as `togglePaused`. Set `target`, `repeatable`, or `disabled` to change those behaviors. The hook still requires a surrounding `Player` for its coordinator.
## Report an action
Pass `action`, and optionally `value`, to report the shortcut to input indicators and to [`useHotkeyShortcut`](https://videojs.org/docs/framework/react/reference/api/use-hotkey-shortcut). The callback still performs the action.
```tsx
function NextChapterHotkey() {
useHotkey({
keys: "n",
action: "nextChapter",
onActivate: () => goToNextChapter(),
});
// The hook must run inside a component, but this one has no UI to render.
return null;
}
```
Custom action names reach indicators unchanged. A [`StatusIndicator`](https://videojs.org/docs/framework/react/reference/components/status-indicator#custom-actions) shows feedback for them when you derive a custom status. Built-in names keep their built-in feedback: `action: "seekStep"` shows the default seek step unless you pass a matching `value`.
## API Reference
`useHotkey(options): void`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `options` (required) | `{ keys: string; onActivate: ((event: KeyboardEvent, key: string) => void); target?: 'player' \| 'document'; repeatable?: boolean; disabled?: boolean; action?: 'togglePaused' \| 'toggleMuted' \| 'toggleFullscreen' \| 'toggleSubtitles' \| 'togglePictureInPicture' \| 'toggleControls' \| 'seekStep' \| 'seekToPercent' \| 'volumeStep' \| 'speedUp' \| 'speedDown' \| string & {}; value?: number }` | — | Shortcut keys, activation callback, scope, repeat behavior, disabled state, and the optional action name and value reported to input indicators. |
### Return Value
`void`
---
# useHotkeyShortcut
Hook that reads display and ARIA metadata for a registered Player hotkey
## Import
```tsx
import { useHotkeyShortcut } from "@videojs/react";
```
## Usage
Look up a registered action to keep a custom control’s visible shortcut and `aria-keyshortcuts` value synchronized.
```tsx
function PlayShortcut() {
const { aria, shortcut } = useHotkeyShortcut("togglePaused");
return Play ({shortcut}) ;
}
```
The hook subscribes to registration changes and returns `HotkeyShortcutDetails`: `aria` for the `aria-keyshortcuts` value and `shortcut` for display text. Both are `undefined` when the player container or action is unavailable, or the action has no registered shortcut; pass `value` for actions with value-specific registrations.
## API Reference
`useHotkeyShortcut(action, value?): HotkeyShortcutDetails`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `action` (required) | `string \| undefined` | — | Hotkey action to look up. Returns empty details when absent. |
| `value` | `number` | — | Optional action value used to match value-dependent shortcuts. |
### Return Value
| Property | Type |
| --- | --- |
| `aria` | `string \| undefined` |
| `shortcut` | `string \| undefined` |
---
# I18nProvider
React provider that resolves locale and supplies a typed translator to descendants
## Import
```tsx
import { I18nProvider } from "@videojs/react";
```
`I18nProvider` resolves the active locale, lazy-loads built-in packs, merges registry and provider layers, and exposes a translator through context. Wrap it around a player or custom controls that should translate.
Wrap custom controls or force a locale explicitly:
```tsx
import { I18nProvider } from '@videojs/react/i18n';
```
Omit `locale` to inherit the nearest `lang` attribute (via `langRootRef` or ``). English is the fallback when no non-English locale is active or no translation layer supplies a string. See [Internationalize the player](https://videojs.org/docs/framework/react/guides/internationalization) for merge priority, browser translation fallback, and SSR guidance.
`translations` takes a partial `Translations` map in the nested shape described in [Translation phrases](https://videojs.org/docs/framework/react/reference/api/translation-phrases). `onActiveLocaleChange` receives the resolved `Locale` tag.
The provider supplies its translator and locale through `I18nContext`. Read them with [`useTranslator`](https://videojs.org/docs/framework/react/reference/api/use-translator) and [`useLocale`](https://videojs.org/docs/framework/react/reference/api/use-locale), which fall back to English outside a provider. Read `I18nContext` with `useContext` only when a component needs to know whether a provider is mounted: its value is `null` without one.
## API Reference
`I18nProvider(props): ReactNode`
### Parameters
| Parameter | Type | Default |
| --- | --- | --- |
| `props` (required) | `{ locale?: (typeof LOCALES)[number] \| string & {}; langRootRef?: RefObject; translations?: Partial; children: ReactNode; onActiveLocaleChange?: ((locale: Locale) => void) }` | — |
### Return Value
`ReactNode`
---
# createI18n
Factory that creates framework i18n helpers with custom loading options
## Import
```tsx
import { createI18n } from "@videojs/react/i18n";
```
`createI18n` returns an `I18nProvider`, `useTranslator`, and `useLocale` wired to the shared React i18n context used by the stock skins and controls. Use it when you need options such as a custom locale loader. Most apps use the default exports from `@videojs/react/i18n`.
```tsx
import { createI18n } from '@videojs/react/i18n';
const loaders = {
ja: () => import('@videojs/react/i18n/locales/ja'),
};
const { I18nProvider, useTranslator } = createI18n({
loader: async (tag) => {
const load = loaders[tag as keyof typeof loaders];
return load ? (await load()).default : undefined;
},
});
function App() {
return (
);
}
```
## API Reference
`createI18n(options?): CreateI18nResult`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `options` | `{ loader?: ((tag: string) => Promise \| undefined>) }` | — | Optional hooks such as custom built-in locale loading. |
### Return Value
| Property | Type |
| --- | --- |
| `I18nContext` | `Context` |
| `I18nProvider` | `((props: I18nProviderProps) => ReactNode)` |
| `useTranslator` | `typeof useTranslator` |
| `useLocale` | `typeof useLocale` |
---
# useTranslator
React hook that returns the typed translator for the nearest I18nProvider
## Import
```tsx
import { useTranslator } from "@videojs/react";
```
`useTranslator` returns the `Translator` from the nearest [`I18nProvider`](https://videojs.org/docs/framework/react/reference/api/i18n-provider). Control components pass text descriptors from core `getLabel()`. Custom UI can pass an opaque key with its English fallback.
When no provider is mounted, the hook falls back to English registry strings so standalone demos do not throw.
## Example
Switch the provider locale to see `useTranslator` resolve a simple key and a parameterized key from the nearest provider.
**App.tsx**
```tsx
import { I18nProvider, useTranslator } from '@videojs/react/i18n';
import { useState } from 'react';
import './BasicUsage.css';
const translations = {
es: {
buttons: { play: 'Reproducir' },
seek: { forward: 'Adelantar {seconds} segundos' },
},
fr: {
buttons: { play: 'Lire' },
seek: { forward: 'Avancer de {seconds} secondes' },
},
} as const;
type Locale = keyof typeof translations;
function Labels() {
const t = useTranslator();
return (
Button
{t('buttons.play', { default: 'Play' })}
Parameterized
{t('seek.forward', { seconds: 10, default: 'Seek forward {seconds} seconds' })}
);
}
export default function BasicUsage() {
const [locale, setLocale] = useState('es');
return (
Language
setLocale(event.currentTarget.value as Locale)}>
Spanish
French
);
}
```
**App.css**
```css
.react-use-translator-basic {
display: grid;
gap: 16px;
padding: 16px;
}
.react-use-translator-basic label,
.react-use-translator-basic__output div {
display: flex;
gap: 8px;
align-items: center;
}
.react-use-translator-basic select {
padding: 4px 8px;
}
.react-use-translator-basic__output {
display: grid;
gap: 8px;
margin: 0;
}
.react-use-translator-basic__output dt {
color: #6b7280;
}
.react-use-translator-basic__output dd {
margin: 0;
}
```
## API Reference
`useTranslator(): Translator`
### Return Value
`Translator`
---
# useLocale
React hook that returns the active BCP 47 locale from the nearest I18nProvider
## Import
```tsx
import { useLocale } from "@videojs/react";
```
`useLocale` returns the resolved BCP 47 tag from the nearest [`I18nProvider`](https://videojs.org/docs/framework/react/reference/api/i18n-provider), or `'en'` when none is mounted. The `Locale` type autocompletes the built-in locale tags and accepts any other BCP 47 string.
Use it when UI copy depends on the active locale outside the translator, for example formatting or caption language hooks.
## Example
Switch the provider locale to see `useLocale` supply the BCP 47 tag used to format a date.
**App.tsx**
```tsx
import { I18nProvider, useLocale } from '@videojs/react/i18n';
import { useState } from 'react';
import './BasicUsage.css';
const locales = ['en-US', 'fr-FR', 'ja-JP'] as const;
type Locale = (typeof locales)[number];
function LocaleDetails() {
const locale = useLocale();
const date = new Intl.DateTimeFormat(locale, {
dateStyle: 'long',
timeZone: 'UTC',
}).format(new Date('2026-01-15T12:00:00Z'));
return (
Active locale
{locale}
Formatted date
{date}
);
}
export default function BasicUsage() {
const [locale, setLocale] = useState('en-US');
return (
Locale
setLocale(event.currentTarget.value as Locale)}>
{locales.map((value) => (
{value}
))}
);
}
```
**App.css**
```css
.react-use-locale-basic {
display: grid;
gap: 16px;
padding: 16px;
}
.react-use-locale-basic label,
.react-use-locale-basic__output div {
display: flex;
gap: 8px;
align-items: center;
}
.react-use-locale-basic select {
padding: 4px 8px;
}
.react-use-locale-basic__output {
display: grid;
gap: 8px;
margin: 0;
}
.react-use-locale-basic__output dt {
color: #6b7280;
}
.react-use-locale-basic__output dd {
margin: 0;
}
```
## API Reference
`useLocale(): Locale`
### Return Value
`(typeof LOCALES)[number] | string & {}`
---
# Translation phrases
Semantic i18n keys, their English defaults, and the player UI that uses them
Video.js uses opaque semantic keys so English copy can change without breaking translation packs. English defaults are defined in `packages/core/src/core/i18n/locales/en.ts`. Both HTML and React controls use the same keys.
Locale packs and provider overrides use nested objects, so `buttons.play` is authored as `{ buttons: { play: '…' } }`. Translators use the dotted form.
## Text descriptors
Each key also ships as a text descriptor: an object with the `key` and its English default `text`. Descriptors are grouped by the key’s first segment and named after the rest, so `buttons.play` is `playText` in the `buttons` module and `container.label` is `labelText` in `container`. Pass one to a translator, or to [`translateText`](https://videojs.org/docs/framework/react/reference/api/translate-text), to get the active locale’s copy with English as the fallback.
```tsx
import { useTranslator } from '@videojs/react/i18n';
import { playText } from '@videojs/react/i18n/text/buttons';
function PlayLabel() {
const t = useTranslator();
return {t(playText)} ;
}
```
## Playback controls
| Key | English default | Used by |
| --- | --- | --- |
| `buttons.play` | Play | Play button and its tooltip |
| `buttons.pause` | Pause | Play button and its tooltip |
| `buttons.replay` | Replay | Play button and its tooltip |
| `buttons.mute` | Mute | Mute button and its tooltip |
| `buttons.unmute` | Unmute | Mute button and its tooltip |
| `seek.forward` | Seek forward `{seconds}` seconds | Seek-forward button and its tooltip |
| `seek.backward` | Seek backward `{seconds}` seconds | Seek-backward button and its tooltip |
| `fullscreen.enter` | Enter fullscreen | Fullscreen button |
| `fullscreen.exit` | Exit fullscreen | Fullscreen button |
| `captions.enable` | Enable captions | Captions button and captions selection control |
| `captions.disable` | Disable captions | Captions button and captions selection control |
| `pip.enter` | Enter picture-in-picture | Picture-in-picture button |
| `pip.exit` | Exit picture-in-picture | Picture-in-picture button |
| `live.playing` | Playing live | Live button |
| `live.seekToEdge` | Seek to live edge | Live button |
| `live.badge` | Live | Live button badge |
| `cast.start` | Start casting | Cast button |
| `cast.stop` | Stop casting | Cast button |
| `cast.connecting` | Connecting | Cast button |
| `airplay.start` | Start AirPlay | AirPlay button |
| `airplay.stop` | Stop AirPlay | AirPlay button |
| `container.label` | Media player | Player container aria label |
## Time and volume
| Key | English default | Used by |
| --- | --- | --- |
| `slider.seek` | Seek | Time slider aria label |
| `time.current` | Current time | Current-time display aria label |
| `time.duration` | Duration | Duration display aria label |
| `time.remaining` | Remaining | Remaining-time display aria label |
| `time.elapsedSuffix` | `{duration} elapsed` | Elapsed time in a toggleable display aria label |
| `time.durationSuffix` | `{duration} duration` | Duration in a toggleable display aria label |
| `time.remainingSuffix` | `{duration} remaining` | Remaining time display |
| `time.showElapsed` | `Show elapsed time, {duration}.` | Toggleable time display aria label |
| `time.showDuration` | `Show duration, {duration}.` | Toggleable time display aria label |
| `time.showRemaining` | `Show remaining time, {duration}.` | Toggleable time display aria label |
| `time.toggleElapsed` | `Toggle between elapsed and remaining time.` | Elapsed-time toggle aria description |
| `time.toggleDuration` | `Toggle between duration and remaining time.` | Duration toggle aria description |
| `time.position` | `{current} of {duration}` | Time slider value text |
| `time.unknown` | Media not loaded, unknown time. | Time display aria label and time slider value text before a duration is known |
| `playback.rate` | Playback rate `{rate}` | Playback-rate button and radio group |
| `volume.mutedValue` | `{percent}, muted` | Muted volume slider value text |
| `volume.muted` | Muted | Volume input feedback |
| `volume.label` | Volume | Volume slider aria label |
| `volume.value` | Volume `{value}` | Volume input feedback |
## Playback feedback
| Key | English default | Used by |
| --- | --- | --- |
| `status.captionsOn` | Captions on | Input feedback after enabling captions |
| `status.captionsOff` | Captions off | Input feedback after disabling captions |
| `status.paused` | Paused | Input feedback after pausing |
| `status.playing` | Playing | Input feedback after starting playback |
| `status.fullscreen` | Fullscreen | Input feedback after entering fullscreen |
| `status.pip` | Picture in picture | Input feedback after entering picture-in-picture |
| `status.exitPip` | Exit picture in picture | Input feedback after exiting picture-in-picture |
| `status.seekedTo` | Seeked to `{time}` | Status announcer after a seek |
## Errors
| Key | English default | Used by |
| --- | --- | --- |
| `errors.aborted` | You stopped media playback before it finished. | Media error dialog for `MEDIA_ERR_ABORTED` |
| `errors.network` | This media could not be loaded due to a network or server issue. | Media error dialog for `MEDIA_ERR_NETWORK` |
| `errors.decode` | This media could not be played. It may be corrupted, or your browser may not support its format. | Media error dialog for `MEDIA_ERR_DECODE` |
| `errors.source` | This media could not be loaded. It may be unavailable, or your browser may not support its format. | Media error dialog for `MEDIA_ERR_SRC_NOT_SUPPORTED` |
| `errors.encrypted` | This media could not be played because it could not be decrypted. | Media error dialog for `MEDIA_ERR_ENCRYPTED` |
| `errors.unplayable` | This media is unsupported by the player. | Media error dialog when the player cannot play a source the browser supports |
| `errors.title` | Something went wrong. | Generic media error dialog title |
| `errors.unexpected` | An unexpected error occurred. | Generic media error dialog fallback description |
| `common.empty` | `''` | Empty description for a custom media error |
| `common.ok` | OK | Error dialog confirmation button |
## Settings and track menus
| Key | English default | Used by |
| --- | --- | --- |
| `menu.settings` | Settings | Video settings menu trigger |
| `menu.quality` | Quality | Quality menu |
| `menu.audio` | Audio | Audio-track menu and fallback track label |
| `menu.default` | Default | Reserved default-option label; no stock control currently emits it |
| `menu.speed` | Speed | Playback-rate menu |
| `menu.captions` | Captions | Captions menu |
| `menu.playbackRate` | Playback rate | Playback-rate radio group |
| `menu.back` | Back | Nested settings-menu back button |
| `menu.off` | Off | Captions menu option |
| `menu.auto` | Auto | Automatic-quality option |
| `menu.autoWithLabel` | Auto (`{label}`) | Automatic-quality option with the active rendition |
| `menu.subtitles` | Subtitles | Subtitles track option |
## Related pages
- [Internationalize the player](https://videojs.org/docs/framework/react/guides/internationalization): Translate player labels and announcements: shipped locale packs, custom translations, runtime switching, and server rendering.
---
# registerI18n
Register or merge translation strings for a BCP 47 locale tag in the global i18n registry
## Import
```tsx
import { registerI18n } from "@videojs/react/i18n";
```
`registerI18n` merges a partial translation map into the process-wide registry for a locale tag. Components carry their own English fallback text. Built-in non-English packs lazy-load automatically; call `registerI18n` for custom locales, CDN locale modules, or patched shipped packs before the provider renders.
Keys are nested semantic names such as `buttons.play` and `buttons.pause`. See [Translation phrases](https://videojs.org/docs/framework/react/reference/api/translation-phrases) for the complete catalog.
```ts
import { registerI18n } from '@videojs/react/i18n';
registerI18n('es', {
buttons: {
play: 'Reproducir',
pause: 'Pausar',
},
});
```
## Built-in locale packs
Each shipped pack is a module named after its locale tag. Its default export is the pack’s `Translations` map. Pass it to `registerI18n` when you load a pack yourself, for example right before switching to that locale:
```ts
import { registerI18n } from '@videojs/react/i18n';
import ja from '@videojs/react/i18n/locales/ja';
registerI18n('ja', ja);
```
A side-effect import of the pack’s `/register` subpath, such as `import '@videojs/react/i18n/locales/ja/register'`, does the same.
## API Reference
`registerI18n(locale, translations): void`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `locale` (required) | `(typeof LOCALES)[number] \| string & {}` | — | BCP 47 tag (normalized to lowercase; unicode extensions stripped for the registry key). |
| `translations` (required) | `Partial` | — | Partial nested locale values; merges with any existing layer for the tag. |
### Return Value
`void`
---
# getI18nTranslations
Read the merged translation map for a locale using BCP 47 parent-chain fallback
## Import
```tsx
import { getI18nTranslations } from "@videojs/react/i18n";
```
`getI18nTranslations` walks the BCP 47 lookup chain (`es-MX` → `es` → `en`) and merges registry layers into one map. Providers combine this map with lazy-loaded built-in packs and provider overrides. Call it directly when building custom UI outside built-in controls.
See [Internationalize the player](https://videojs.org/docs/framework/react/guides/internationalization) for merge order and fallback rules.
```ts
import { createTranslator, getI18nTranslations } from '@videojs/react/i18n';
const t = createTranslator(getI18nTranslations('pt-BR'), 'pt-BR');
t('buttons.play', { default: 'Play' });
```
## API Reference
`getI18nTranslations(locale): FlatTranslations`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `locale` (required) | `(typeof LOCALES)[number] \| string & {}` | — | BCP 47 tag to resolve (e.g. `es-MX`, `zh-Hant-HK`). |
### Return Value
| Type |
| --- |
| `{ [Key in TranslationKey]?: TranslationParams[Key] extends never ? string : Key extends keyof ParametricTranslations ? ParametricTranslations[Key] : string; } & Record` |
---
# hasRegisteredLocale
Check whether an exact locale tag exists in the global i18n registry
## Import
```tsx
import { hasRegisteredLocale } from "@videojs/react/i18n";
```
`hasRegisteredLocale` returns whether a normalized locale tag has an explicit registry layer from [`registerI18n`](https://videojs.org/docs/framework/react/reference/api/register-i18n). It does **not** indicate whether a lazy built-in pack exists. Only registry entries count.
```ts
import { hasRegisteredLocale, registerI18n } from '@videojs/react/i18n';
hasRegisteredLocale('fr'); // false until registered
registerI18n('fr', { buttons: { play: 'Lecture' } });
hasRegisteredLocale('fr'); // true
```
## API Reference
`hasRegisteredLocale(locale): boolean`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `locale` (required) | `(typeof LOCALES)[number] \| string & {}` | — | BCP 47 tag to test. |
### Return Value
`boolean`
---
# onI18nRegistryChange
Subscribe to global i18n registry mutations
## Import
```tsx
import { onI18nRegistryChange } from "@videojs/react/i18n";
```
`onI18nRegistryChange` registers a callback that runs whenever any locale layer changes, for example after `registerI18n` or browser translation prefetch. Returns an unsubscribe function.
React `I18nProvider` uses this to invalidate translators when the registry updates.
## API Reference
`onI18nRegistryChange(callback): (() => void)`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `callback` (required) | `(() => void)` | — | Invoked when any locale layer changes. |
### Return Value
| Type |
| --- |
| `(() => void)` |
---
# createTranslator
Build a typed translator from a resolved translation map
## Import
```tsx
import { createTranslator } from "@videojs/react/i18n";
```
`createTranslator` wraps the flat map returned by `getI18nTranslations` and returns a typed `Translator` function. Providers call this internally after merging registry, lazy-loaded built-in packs, browser-translated fallback copy, and provider layers. Use it directly for custom UI outside built-in controls.
```ts
import { createTranslator, getI18nTranslations } from '@videojs/react/i18n';
const t = createTranslator(getI18nTranslations('fr'), 'fr');
t('buttons.play', { default: 'Play' });
t('seek.forward', { seconds: 5, default: 'Seek forward {seconds} seconds' });
```
## API Reference
`createTranslator(translations, locale): Translator`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `translations` (required) | `{ [Key in TranslationKey]?: TranslationParams[Key] extends never ? string : Key extends keyof ParametricTranslations ? ParametricTranslations[Key] : string; } & Record` | — | Merged translation map for the active locale. |
| `locale` (required) | `(typeof LOCALES)[number] \| string & {}` | — | BCP 47 tag associated with the map (reserved for future locale-aware behavior). |
### Return Value
| Type |
| --- |
| `{ (key: Key, ...args: Key extends TranslationKey ? TranslationParams[Key] extends never ? [params?: TranslationOptions] : [params: TranslationParams[Key] & TranslationOptions] : [params?: TextParams & TranslationOptions]): string; (text: Text, params?: TextParams): string }` |
---
# translateText
Translate a text descriptor, or fall back to its English default
## Import
```tsx
import { translateText } from "@videojs/react/i18n";
```
`translateText` translates a text descriptor with a translator from [`createTranslator`](https://videojs.org/docs/framework/react/reference/api/create-translator). Without one, it fills the descriptor’s English default with the params. A plain string is returned as is, so a label can be either.
```ts
import { createTranslator, getI18nTranslations, translateText } from '@videojs/react/i18n';
import { forwardText } from '@videojs/react/i18n/text/seek';
translateText(forwardText, { seconds: 10 }); // 'Seek forward 10 seconds'
const t = createTranslator(getI18nTranslations('fr'), 'fr');
translateText(forwardText, t, { seconds: 10 });
```
## API Reference
### Overload 1
`translateText(text, params?): string`
Translate a text descriptor with a translator, or interpolate its English default without one. A plain string is returned as is.
#### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `text` (required) | `{ key: string; text: string } \| string` | — | Text descriptor, or a string returned unchanged. |
| `params` | `Record` | — | Values for the text's `{placeholders}`. |
#### Return Value
`string`
### Overload 2
`translateText(text, translator, params?): string`
#### Parameters
| Parameter | Type | Default |
| --- | --- | --- |
| `text` (required) | `{ key: string; text: string } \| string` | — |
| `translator` (required) | `{ (key: Key, ...args: Key extends TranslationKey ? TranslationParams[Key] extends never ? [params?: TranslationOptions] : [params: TranslationParams[Key] & TranslationOptions] : [params?: TextParams & TranslationOptions]): string; (text: Text, params?: TextParams): string } \| undefined` | — |
| `params` | `Record` | — |
#### Return Value
`string`
---
# resolveTranslation
Translate a key with the params its English default requires
## Import
```tsx
import { resolveTranslation } from "@videojs/react/i18n";
```
`resolveTranslation` calls a translator from [`createTranslator`](https://videojs.org/docs/framework/react/reference/api/create-translator) with a key and its params. Unlike calling the translator directly with a variable key, its type requires the params that key’s English default interpolates, such as `seconds` for `seek.forward`.
```ts
import { createTranslator, getI18nTranslations, resolveTranslation } from '@videojs/react/i18n';
const t = createTranslator(getI18nTranslations('fr'), 'fr');
resolveTranslation(t, 'buttons.play');
resolveTranslation(t, 'seek.forward', { seconds: 10 });
```
## API Reference
`resolveTranslation(translator, key, ...args): string`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `translator` (required) | `{ (key: Key, ...args: Key extends TranslationKey ? TranslationParams[Key] extends never ? [params?: TranslationOptions] : [params: TranslationParams[Key] & TranslationOptions] : [params?: TextParams & TranslationOptions]): string; (text: Text, params?: TextParams): string }` | — | Translator from `createTranslator`. |
| `key` (required) | `Key` | — | Translation key, such as `seek.forward`. |
| `args` | `Key extends TranslationKey ? TranslationParams[Key] extends never ? [params?: TranslationOptions] : [params: TranslationParams[Key] & TranslationOptions] : [params?: Record & TranslationOptions]` | — | The key's template params, plus an optional `default` string. |
### Return Value
`string`
---
# isText
Check whether a value is a text descriptor
## Import
```tsx
import { isText } from "@videojs/react/i18n";
```
`isText` returns whether a value is a text descriptor: an object with a translation `key` and its English default `text`. Use it to accept either a descriptor or a plain string, as [`translateText`](https://videojs.org/docs/framework/react/reference/api/translate-text) does. See [Translation phrases](https://videojs.org/docs/framework/react/reference/api/translation-phrases) for the built-in descriptors.
```ts
import { isText } from '@videojs/react/i18n';
import { playText } from '@videojs/react/i18n/text/buttons';
isText(playText); // true
isText('Play'); // false
```
## API Reference
`isText(value): value is Text`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `value` (required) | `unknown` | — | Value to check. |
### Return Value
`value is Text`
---
# loadLocale
Lazy-load the built-in translation pack for a locale
## Import
```tsx
import { loadLocale } from "@videojs/react/i18n";
```
`loadLocale` imports the built-in pack for a tag, or for the closest tag in its [`findLocaleKeys`](https://videojs.org/docs/framework/react/reference/api/find-locale-keys) chain, and resolves to its flat translations. It resolves to `undefined` when that chain reaches a locale registered with [`registerI18n`](https://videojs.org/docs/framework/react/reference/api/register-i18n) first, or when no built-in pack matches. Providers call it for you; use it to translate UI outside a provider without bundling every pack.
```ts
import { createTranslator, getI18nTranslations, loadLocale } from '@videojs/react/i18n';
const pack = await loadLocale('fr-CA');
const t = createTranslator({ ...pack, ...getI18nTranslations('fr-CA') }, 'fr-CA');
t('buttons.play'); // 'Lecture'
```
## API Reference
`loadLocale(tag): Promise | undefined>`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `tag` (required) | `string` | — | BCP 47 tag to load, such as `fr-CA`. |
### Return Value
| Type |
| --- |
| `Promise \| undefined>` |
---
# getLocaleKey
Normalize a locale tag to the key the i18n registry stores it under
## Import
```tsx
import { getLocaleKey } from "@videojs/react/i18n";
```
`getLocaleKey` lowercases a BCP 47 tag, replaces underscores with hyphens, and removes Unicode extensions such as `-u-nu-latn`. The registry stores [`registerI18n`](https://videojs.org/docs/framework/react/reference/api/register-i18n) layers under this key, so use it to compare a tag with one you have registered or to key your own per-locale data the same way.
```ts
import { getLocaleKey } from '@videojs/react/i18n';
getLocaleKey('en_US-u-nu-latn'); // 'en-us'
getLocaleKey('zh-Hant'); // 'zh-hant'
```
## API Reference
`getLocaleKey(locale): Locale`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `locale` (required) | `(typeof LOCALES)[number] \| string & {}` | — | BCP 47 tag to normalize. |
### Return Value
`(typeof LOCALES)[number] | string & {}`
---
# findLocaleKeys
List the locale tags a lookup falls back through, most specific first
## Import
```tsx
import { findLocaleKeys } from "@videojs/react/i18n";
```
`findLocaleKeys` returns the fallback chain the player resolves a locale through: the normalized tag from [`getLocaleKey`](https://videojs.org/docs/framework/react/reference/api/get-locale-key), then each shorter prefix, ending with `en`. A Chinese tag with a script, such as `zh-Hant-HK`, also falls back through `zh-tw` or `zh-cn` before `zh`. A translation missing from one locale is looked up in the next.
```ts
import { findLocaleKeys } from '@videojs/react/i18n';
findLocaleKeys('es-419-u-nu-latn'); // ['es-419', 'es', 'en']
findLocaleKeys('fr'); // ['fr', 'en']
```
## API Reference
`findLocaleKeys(locale): Locale[]`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `locale` (required) | `(typeof LOCALES)[number] \| string & {}` | — | BCP 47 tag to resolve. |
### Return Value
`Locale[]`
---
# resolveAdapterType
Resolve which type of adapter plays a source URL, from its host, MIME type, or file extension
## Import
```tsx
import { resolveAdapterType } from '@videojs/react';
```
`resolveAdapterType` reads a source URL and returns the type of adapter that plays it, so an app that plays sources from a catalog or from user input can pick the matching [media component](https://videojs.org/docs/framework/react/guides/media-sources). It returns `null` for sources it doesn’t recognize.
Resolution is a pure string check. It doesn’t fetch the source, load an engine, or touch the DOM. It doesn’t rewrite the source either: every source it recognizes, [shorthands](https://videojs.org/docs/framework/react/reference/api/resolve-adapter-type#shorthands) included, is one the matching media component accepts as its `src`.
## Usage
Switch on the result and render the media component for it with the same `src`:
```tsx
import { resolveAdapterType } from '@videojs/react';
import { HlsJsVideo } from '@videojs/react/media/hlsjs-video';
import { VimeoVideo } from '@videojs/react/media/vimeo-video';
import { YouTubeVideo } from '@videojs/react/media/youtube-video';
import { Video } from '@videojs/react/video';
function Media({ src }: { src: string }) {
switch (resolveAdapterType(src)) {
case 'youtube':
return ;
case 'vimeo':
return ;
case 'hls':
return ;
case 'video':
return ;
default:
return null;
}
}
```
Each media component you import adds its engine to your bundle, so load the rarely used ones with `React.lazy`.
### Adapter types
| Adapter type | Recognized sources | Media component |
| --- | --- | --- |
| `youtube` | `youtube.com`, `youtu.be`, and `youtube-nocookie.com` video and playlist URLs | `YouTubeVideo` |
| `vimeo` | `vimeo.com` and `player.vimeo.com` URLs | `VimeoVideo` |
| `wistia` | `wistia.com`, `wistia.net`, and `wi.st` URLs, and pages with a `wvideo` parameter | `WistiaVideo` |
| `mux` | `stream.mux.com` stream URLs, with or without `.m3u8` | `MuxVideo` or `MuxAudio` |
| `cloudflare` | `cloudflarestream.com` and `videodelivery.net` URLs | `CloudflareVideo` |
| `spotify` | `open.spotify.com` URLs and `spotify:` URIs | `SpotifyAudio` |
| `tiktok` | `tiktok.com` video URLs | `TikTokVideo` |
| `twitch` | `twitch.tv` video and channel URLs | `TwitchVideo` |
| `hls` | `.m3u8` files and HLS MIME types | `HlsVideo`, `HlsJsVideo`, or `NativeHlsVideo` |
| `dash` | `.mpd` files and `application/dash+xml` | `DashVideo` or `ShakaVideo` |
| `video` | `.mp4`, `.webm`, `.mov`, and `.ogv` files, and `video/*` MIME types | `Video` |
| `audio` | `.mp3`, `.m4a`, `.wav`, `.ogg`, `.flac`, and `.aac` files, and `audio/*` MIME types | `Audio` |
`hls` covers every HLS adapter and `dash` every DASH adapter, so an HLS stream comes back as `hls` whichever one you play it with; see [Media sources](https://videojs.org/docs/framework/react/guides/media-sources) to choose one. Mux streams served from a custom domain are recognized as plain `hls`.
### MIME types
Pass a MIME type as the second argument when the URL doesn’t end in a file extension, such as an extensionless manifest or a `blob:` URL. The MIME type takes precedence over the extension; service URLs, such as YouTube or Mux, resolve the same either way.
```ts
resolveAdapterType('https://example.com/manifest', 'application/vnd.apple.mpegurl'); // 'hls'
```
To get the MIME type a file extension implies, use [`resolveMimeType`](https://videojs.org/docs/framework/react/reference/api/resolve-mime-type).
### Bare ids
A bare id returns `null`. An 11-character YouTube id and a 10-character Wistia id look alike, so services are only recognized from URLs, `spotify:` URIs, and [shorthands](https://videojs.org/docs/framework/react/reference/api/resolve-adapter-type#shorthands). If you already know the service, pass the id to its media component directly.
### Shorthands
`resolveAdapterType` recognizes these shorthands, and the YouTube and Vimeo media accept them as `src`:
| Shorthand | Plays |
| --- | --- |
| `youtube/`, `youtube/shorts/` | The YouTube video, from the privacy-enhanced `youtube-nocookie.com` host |
| `vimeo/` | The Vimeo video |
| `vimeo/?hash=`, `vimeo/?h=`, `vimeo//` | The unlisted Vimeo video |
## API Reference
`resolveAdapterType(src, type?): AdapterType | null`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `src` (required) | `string` | — | The source URL, or a `youtube/` or `vimeo/` shorthand. |
| `type` | `string` | — | The source's MIME type, when known. It takes precedence over the file extension, so manifests and files without one can still be resolved. Parameters such as `codecs` are ignored. |
### Return Value
| Type |
| --- |
| `'youtube' \| 'vimeo' \| 'wistia' \| 'mux' \| 'cloudflare' \| 'spotify' \| 'tiktok' \| 'twitch' \| 'hls' \| 'dash' \| 'video' \| 'audio' \| null` |
---
# resolveMimeType
Resolve a source's MIME type from its file extension
## Import
```tsx
import { resolveMimeType } from '@videojs/react';
```
`resolveMimeType` returns the MIME type a source URL’s file extension implies, ignoring the query string and hash. It returns `null` when the URL has no extension or one it doesn’t recognize.
```ts
resolveMimeType('https://example.com/live/stream.m3u8?token=abc'); // 'application/x-mpegurl'
resolveMimeType('/media/podcast.mp3'); // 'audio/mpeg'
resolveMimeType('https://youtu.be/_cMxraX_5RE'); // null
```
| Extension | MIME type |
| --- | --- |
| `.m3u8` | `application/x-mpegurl` |
| `.mpd` | `application/dash+xml` |
| `.mp4` | `video/mp4` |
| `.webm` | `video/webm` |
| `.mov` | `video/quicktime` |
| `.ogv` | `video/ogg` |
| `.mp3` | `audio/mpeg` |
| `.m4a` | `audio/mp4` |
| `.wav` | `audio/wav` |
| `.ogg` | `audio/ogg` |
| `.flac` | `audio/flac` |
| `.aac` | `audio/aac` |
To find which media component plays a source, use [`resolveAdapterType`](https://videojs.org/docs/framework/react/reference/api/resolve-adapter-type).
## API Reference
`resolveMimeType(src): string | null`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `src` (required) | `string` | — | The source URL. |
### Return Value
`string | null`
---
# useButton
Hook for creating accessible button components with keyboard and pointer interaction
## Import
```tsx
import { useButton } from "@videojs/react";
```
`useButton` provides button behavior including keyboard activation and accessibility checks. It returns a `getButtonProps` function for spreading onto a `` element and a `buttonRef` for validation.
`getButtonProps` merges internal button props (click/keyboard handlers, disabled state) with any external props you pass in. Use [`renderElement`](https://videojs.org/docs/framework/react/reference/api/render-element) to render the button with state-driven props and render prop support. In development mode, `buttonRef` warns if the rendered element is not a ``.
## Examples
### Basic Usage
**App.tsx**
```tsx
import { useButton } from '@videojs/react';
import type { Ref } from 'react';
import { useState } from 'react';
export default function BasicUsage() {
const [count, setCount] = useState(0);
const [disabled, setDisabled] = useState(false);
const { getButtonProps, buttonRef } = useButton({
displayName: 'ActivateButton',
onActivate: () => setCount((c) => c + 1),
isDisabled: () => disabled,
});
return (
} {...getButtonProps()} className="button" disabled={disabled}>
Activated {count} times
setDisabled(e.target.checked)} />
Disabled
);
}
```
**App.css**
```css
.demo {
display: flex;
flex-direction: column;
gap: 12px;
padding: 16px;
}
.button {
align-self: flex-start;
padding: 8px 20px;
font-variant-numeric: tabular-nums;
color: #111827;
cursor: pointer;
background: #f5f5f5;
border: 1px solid #ccc;
border-radius: 6px;
transition: opacity 0.2s;
}
.button[disabled] {
cursor: not-allowed;
opacity: 0.5;
}
.label {
display: flex;
gap: 6px;
align-items: center;
font-size: 0.875rem;
color: #6b7280;
}
```
## API Reference
`useButton(params): UseButtonReturnValue`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `params` (required) | `{ displayName: string; onActivate: ((event: UIEvent, source: ButtonActivationSource) => void); isDisabled: (() => boolean) }` | — | Button configuration with activation handler and disabled check. |
### Return Value
| Property | Type |
| --- | --- |
| `getButtonProps` | `((externalProps?: ComponentPropsWithRef<'button'>) => ComponentPropsWithRef<'button'>)` |
| `buttonRef` | `Ref` |
---
# useSlider
Hook for connecting slider input behavior to custom React elements
`useSlider` is the low-level hook behind the [Slider](https://videojs.org/docs/framework/react/reference/components/slider), [TimeSlider](https://videojs.org/docs/framework/react/reference/components/time-slider), and [VolumeSlider](https://videojs.org/docs/framework/react/reference/components/volume-slider) components. Use it when you need to build a new slider primitive with custom state calculations. For standard player controls, use the built-in components.
## Import
```tsx
import { useSlider } from "@videojs/react";
```
## Usage
Pass callbacks that convert raw slider input into your component state, values, and CSS variables. Attach the returned root props, ref, and styles to the pointer target. Attach the thumb props and ref to the focusable thumb.
```tsx
function CustomSlider({ options }: { options: useSlider.Options }) {
const {
state,
input,
cssVars,
rootRef,
thumbRef,
rootProps,
rootStyle,
thumbProps,
} = useSlider(options);
return (
);
}
```
`state` is the derived state for the current render. `input` is the underlying reactive state container. Read `input.current` for the latest raw input or call `input.subscribe(listener)` when another system needs to observe changes outside React rendering.
## API Reference
`useSlider(options): UseSliderReturnValue`
### Parameters
| Parameter | Type | Default |
| --- | --- | --- |
| `options` (required) | `{ getPercent: (() => number); getStepPercent: (() => number); getLargeStepPercent: (() => number); changeThrottle?: number; onValueChange?: ((percent: number) => void); onValueCommit?: ((percent: number) => void); onPressStart?: (() => void); onPressEnd?: (() => void); onDragStart?: (() => void); onDragEnd?: (() => void); computeState: ((input: SliderInput) => State); orientation?: 'horizontal' \| 'vertical'; disabled?: boolean; adjustPercent?: ((rawPercent: number, thumbSize: number, trackSize: number) => number); getCSSVars: ((state: State) => Record) }` | — |
### Return Value
| Property | Type |
| --- | --- |
| `state` | `State` |
| `input` | `State` |
| `cssVars` | `Record` |
| `rootRef` | `RefCallback` |
| `thumbRef` | `RefCallback` |
| `rootProps` | `{ onPointerDown: ((event: UIPointerEvent) => void); onPointerMove: ((event: UIPointerEvent) => void); onPointerUp: ((event: UIPointerEvent) => void); onPointerLeave: ((event: UIPointerEvent) => void); onLostPointerCapture: (() => void) }` |
| `rootStyle` | `{ touchAction: string; userSelect: string }` |
| `thumbProps` | `{ onKeyDownCapture: ((event: UIKeyboardEvent) => void); onFocus: (() => void); onBlur: (() => void) }` |
---
# useComposedRefs
Hook that updates multiple React refs through one stable callback ref
## Import
```tsx
import { useComposedRefs } from "@videojs/react";
```
## Usage
Compose an internal ref with a forwarded ref when both need the same element.
```tsx
import { forwardRef, useRef } from "react";
import { useComposedRefs } from "@videojs/react";
const Video = forwardRef(function Video(_, forwardedRef) {
const localRef = useRef(null);
const ref = useComposedRefs(forwardedRef, localRef);
return ;
});
```
The callback updates callback refs and ref objects. It also preserves cleanup functions returned by React 19 callback refs.
## API Reference
`useComposedRefs(...refs): RefCallback`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `refs` | `(Ref \| undefined)[]` | — | Refs to update from the returned callback. |
### Return Value
`RefCallback`
---
# useLatestRef
Hook that exposes the latest rendered value through a stable ref
## Import
```tsx
import { useLatestRef } from "@videojs/react";
```
## Usage
Use a latest-value ref when a long-lived callback must read current props without recreating the subscription that owns it.
```tsx
import { useEffect } from "react";
import { useLatestRef } from "@videojs/react";
type SubscriberProps = {
onChange(value: number): void;
subscribe(callback: (value: number) => void): () => void;
};
function Subscriber({ onChange, subscribe }: SubscriberProps) {
const onChangeRef = useLatestRef(onChange);
useEffect(() => subscribe((value) => onChangeRef.current(value)), []);
return null;
}
```
The ref object is stable across renders and its `current` property is updated during every render.
## API Reference
`useLatestRef(value): Readonly<{ current: Value }>`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `value` (required) | `Value` | — | Value the ref should expose after each render. |
### Return Value
| Property | Type |
| --- | --- |
| `current` | `Value` |
---
# useDestroy
Hook that destroys an imperative instance after a real React unmount
## Import
```tsx
import { useDestroy } from "@videojs/react";
```
## Usage
Pass an object with a `destroy()` method. Optional setup and teardown callbacks run around the instance’s mounted lifetime.
```tsx
type Destroyable = { destroy(): void };
function ManagedInstance({ instance }: { instance: Destroyable }) {
useDestroy(instance);
return null;
}
```
Destruction is deferred to a macrotask so React StrictMode’s simulated unmount and remount can cancel it. A real unmount runs teardown before `destroy()`.
## API Reference
`useDestroy(instance, setup?, teardown?): void`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `instance` (required) | `{ destroy(): void }` | — | Object with a `destroy()` method. |
| `setup` | `(() => void)` | — | Optional setup called on first mount. Skipped on StrictMode re-mount since the previous setup was never torn down. |
| `teardown` | `(() => void)` | — | Optional teardown called right before `destroy()` on real unmount. Skipped on StrictMode simulated unmount. |
### Return Value
`void`
---
# mergeProps
Utility for combining React props with predictable handler, class, and style precedence
## Import
```tsx
import { mergeProps } from "@videojs/react";
```
## Usage
Pass props in increasing precedence order. Event handlers run right to left, class names concatenate right to left, and rightmost style and other prop values win conflicts.
```tsx
const props = mergeProps(
{ className: "base", onClick: logInternal },
{ className: "custom", onClick: onClick, style: { color: "red" } },
);
return Play ;
```
`mergeProps` does not compose refs; a rightmost `ref` replaces earlier refs. Use `useComposedRefs` when every ref needs the element.
## API Reference
`mergeProps(...propSets): ComponentPropsWithRef`
### Parameters
| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `propSets` | `(ComponentPropsWithRef \| undefined)[]` | — | Props objects to merge in order. |
### Return Value
`ComponentPropsWithRef`
---
# renderElement
Utility for rendering UI component elements with state-driven props and render prop support
## Import
```tsx
import { renderElement } from "@videojs/react";
```
`renderElement` renders a UI component element, handling default tag rendering, render props (element or function), props merging, ref composition, and state-driven `className`/`style`.
**PlayButton.tsx**
```tsx
import { useRef } from "react";
import { renderElement } from "@videojs/react";
function PlayButton({ className, style, render, ...props }) {
const buttonRef = useRef(null);
const state = { paused: true };
return renderElement(
"button",
{ className, style, render },
{
state,
ref: buttonRef,
props: [{ type: "button", "aria-label": "Play" }, props],
},
);
}
```
The `className` and `style` component props accept either static values or functions that receive the current state:
```tsx
(state.paused ? "paused" : "playing")}
style={(state) => ({ opacity: state.paused ? 0.5 : 1 })}
/>
```
The `render` prop lets consumers fully customize the rendered element while preserving all internal props and refs:
```tsx
(
{state.paused ? "Play" : "Pause"}
)}
/>
```
## Types
The props type of every Video.js UI component builds on `UIComponentProps`, and its `render` prop takes a `RenderProp`. `@videojs/react` exports these types:
```tsx
import type { HTMLProps, RenderFunction, RenderProp, UIComponentProps } from "@videojs/react";
```
| Type | Description |
| --- | --- |
| `UIComponentProps` | The props of the intrinsic element `TagName`, with `className` and `style` that also accept a function of `State`, plus an optional `render` prop of type `RenderProp`. |
| `RenderProp` | A React element, or a `RenderFunction`. |
| `RenderFunction` | `(props: Props, state: State) => ReactElement \| null` |
| `HTMLProps` | React’s `HTMLAttributes` plus an optional `ref` to `T`: the props a render function receives. `T` defaults to `any`. |
A render function receives the component’s merged props, including its composed `ref`, and its current state. Spread the props on the element it returns. A render element is cloned with the component’s props merged into its own: event handlers are chained, class names are concatenated, styles are merged, and other props on the element win. Its `ref` is composed with the component’s.
## Examples
### Basic Usage
**App.tsx**
```tsx
import { renderElement } from '@videojs/react';
import { type ReactNode, useState } from 'react';
interface TagState {
active: boolean;
}
function Tag({
className,
style,
render,
active,
children,
}: renderElement.ComponentProps & { active: boolean; children?: ReactNode }) {
const state: TagState = { active };
return renderElement(
'span',
{ className, style, render },
{
state,
props: { children },
stateAttrMap: { active: 'data-active' },
}
);
}
export default function BasicUsage() {
const [active, setActive] = useState(false);
const className = (state: TagState) => `tag${state.active ? ' tag--active' : ''}`;
const style = (state: TagState) => ({
fontSize: state.active ? '1.125rem' : '0.875rem',
});
return (
setActive((prev) => !prev)}>
{active ? 'Deactivate' : 'Activate'}
Default <span>
}>
Element <strong>
{state.active ? 'Active!' : 'Inactive'} }
/>
);
}
```
**App.css**
```css
.demo {
display: flex;
flex-direction: column;
gap: 16px;
padding: 16px;
}
.toggle {
align-self: flex-start;
padding: 6px 16px;
color: #111827;
cursor: pointer;
background: #f5f5f5;
border: 1px solid #ccc;
border-radius: 6px;
}
.tags {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.tag {
display: inline-flex;
align-items: center;
padding: 6px 12px;
color: #374151;
background: #e5e7eb;
border-radius: 9999px;
transition: all 0.2s ease;
}
.tag--active {
color: white;
background: #3b82f6;
}
```
## API Reference
`renderElement(element, componentProps, params): ReactElement | null`
### Parameters
| Parameter | Type | Default |
| --- | --- | --- |
| `element` (required) | `TagName` | — |
| `componentProps` (required) | `{ className?: string \| ((state: State) => string \| undefined); style?: CSSProperties \| ((state: State) => CSSProperties \| undefined); render?: ReactElement \| ((props: HTMLProps, state: State) => ReactElement \| null) }` | — |
| `params` (required) | `{ state: State; ref?: Ref \| Ref[]; props?: object \| object[]; stateAttrMap?: { [Key in keyof State]?: string; } }` | — |
### Return Value
`ReactElement | null`
---
# Error Codes
Error codes emitted by the video.js package and how to resolve them
Playback failures use the [error feature](https://videojs.org/docs/framework/react/reference/api/feature-error) instead. See [Handle playback errors](https://videojs.org/docs/framework/react/guides/playback-errors) for application handling.
## Video.js 8 Error Codes
The `video.js` package throws these `VJS8_LEGACY_*` errors when code calls a Video.js 8 API that does not exist in Video.js 10. Each code identifies the old API and its Video.js 10 equivalent.
- [VJS8_LEGACY_INIT](https://videojs.org/docs/framework/react/reference/api/vjs8-legacy-init)
`videojs()` was the Video.js 8 API. Video.js 10 has no factory; players are components you compose.
- [VJS8_LEGACY_PLUGIN](https://videojs.org/docs/framework/react/reference/api/vjs8-legacy-plugin)
`videojs.registerPlugin()` was the Video.js 8 plugin system. Video.js 10 has no plugin registry.
- [VJS8_LEGACY_COMPONENT](https://videojs.org/docs/framework/react/reference/api/vjs8-legacy-component)
`videojs.registerComponent()` was the Video.js 8 component tree. Video.js 10 components are custom elements and React components.
- [VJS8_LEGACY_GET_PLAYER](https://videojs.org/docs/framework/react/reference/api/vjs8-legacy-get-player)
`videojs.getPlayer()` looked players up by id. Video.js 10 has no registry; hold a reference to the element.
- [VJS8_LEGACY_OPTIONS](https://videojs.org/docs/framework/react/reference/api/vjs8-legacy-options)
`videojs.options` held Video.js 8 global defaults. Video.js 10 has no global; configuration lives on the components you render.
---
# VJS8_LEGACY_INIT
Legacy Video.js 8 player initialization error
`videojs()` was the Video.js 8 API. Video.js 10 has no factory; players are components you compose.
## Trigger
```javascript
const player = videojs('my-video', { controls: true });
```
## Video.js 10 equivalent
`import { Video, VideoPlayer, VideoSkin } from '@videojs/react/video'` and render ` `.
See [Migrate from Video.js 8](https://videojs.org/docs/framework/react/guides/migrate-from-video-js-8) for the full setup.
## Video.js 8
Video.js 8 continues to receive security patches. Its documentation is at [legacy.videojs.org](https://legacy.videojs.org). Pin the major version with `npm install video.js@8` to keep using it.
---
# VJS8_LEGACY_PLUGIN
Legacy Video.js 8 plugin registration error
`videojs.registerPlugin()` was the Video.js 8 plugin system. Video.js 10 has no plugin registry.
## Trigger
```javascript
videojs.registerPlugin('myPlugin', function () { /* ... */ });
```
## Video.js 10 equivalent
Compose behavior as components inside ``, or add an extension such as `@videojs/google-cast`.
See [Migrate from Video.js 8](https://videojs.org/docs/framework/react/guides/migrate-from-video-js-8) for the full setup.
## Video.js 8
Video.js 8 continues to receive security patches. Its documentation is at [legacy.videojs.org](https://legacy.videojs.org). Pin the major version with `npm install video.js@8` to keep using it.
---
# VJS8_LEGACY_COMPONENT
Legacy Video.js 8 component registration error
`videojs.registerComponent()` was the Video.js 8 component tree. Video.js 10 components are custom elements and React components.
## Trigger
```javascript
videojs.registerComponent('MyButton', MyButton);
```
## Video.js 10 equivalent
Write a React component and place it inside `` or your own skin tree.
See [Migrate from Video.js 8](https://videojs.org/docs/framework/react/guides/migrate-from-video-js-8) for the full setup.
## Video.js 8
Video.js 8 continues to receive security patches. Its documentation is at [legacy.videojs.org](https://legacy.videojs.org). Pin the major version with `npm install video.js@8` to keep using it.
---
# VJS8_LEGACY_GET_PLAYER
Legacy Video.js 8 player lookup error
`videojs.getPlayer()` looked players up by id. Video.js 10 has no registry; hold a reference to the element.
## Trigger
```javascript
const player = videojs.getPlayer('my-video');
```
## Video.js 10 equivalent
Select state and actions with `usePlayer()` from `@videojs/react/video` inside the tree.
See [Migrate from Video.js 8](https://videojs.org/docs/framework/react/guides/migrate-from-video-js-8) for the full setup.
## Video.js 8
Video.js 8 continues to receive security patches. Its documentation is at [legacy.videojs.org](https://legacy.videojs.org). Pin the major version with `npm install video.js@8` to keep using it.
---
# VJS8_LEGACY_OPTIONS
Legacy Video.js 8 global options error
`videojs.options` held Video.js 8 global defaults. Video.js 10 has no global; configuration lives on the components you render.
## Trigger
```javascript
videojs.options.autoplay = true;
```
## Video.js 10 equivalent
Pass props to ``, ``, and the media component.
See [Migrate from Video.js 8](https://videojs.org/docs/framework/react/guides/migrate-from-video-js-8) for the full setup.
## Video.js 8
Video.js 8 continues to receive security patches. Its documentation is at [legacy.videojs.org](https://legacy.videojs.org). Pin the major version with `npm install video.js@8` to keep using it.