# Video.js User Guide

Video.js is a free, open-source HTML5 video and audio player for the web. You drop it onto a page, point it at a media file or stream, and get a consistent, skinnable, accessible player that behaves the same across desktops, phones, tablets, and web-based smart TVs. Out of the box it plays standard files (MP4, WebM, Ogg) **and** adaptive streams (HLS and MPEG-DASH) through a bundled streaming engine, and it can be extended with a large ecosystem of plugins.

This guide is for the people who **use** Video.js in a website or app: front-end developers, page authors, and anyone embedding a player. It covers installation, every player option, the JavaScript API you control playback with, captions and audio tracks, skinning, plugins, and troubleshooting. Architecture, build internals, and contributor topics live in the Admin Guide.

> [!NOTE]
> This guide documents Video.js **8.23.8**. The library follows [semantic versioning](https://semver.org/). A handful of helper functions are deprecated in v8 and will be removed in v9.0 — those are flagged throughout and collected in [Deprecated and Removed APIs](#deprecated-and-removed-apis).

---

## Table of Contents

1. [Quick Start](#1-quick-start)
2. [Installation](#2-installation)
3. [Setting Up a Player](#3-setting-up-a-player)
4. [Everyday Playback Control (the API)](#4-everyday-playback-control-the-api)
5. [Player Options Reference](#5-player-options-reference)
6. [Working with Sources and Formats](#6-working-with-sources-and-formats)
7. [Captions, Subtitles, and Other Text Tracks](#7-captions-subtitles-and-other-text-tracks)
8. [Audio and Video Tracks](#8-audio-and-video-tracks)
9. [Responsive Sizing and Layout](#9-responsive-sizing-and-layout)
10. [Languages and Localization](#10-languages-and-localization)
11. [Skinning and Styling](#11-skinning-and-styling)
12. [Components: Customizing the UI](#12-components-customizing-the-ui)
13. [Plugins](#13-plugins)
14. [Removing and Replacing Players](#14-removing-and-replacing-players)
15. [Events Reference](#15-events-reference)
16. [Troubleshooting](#16-troubleshooting)
17. [Frequently Asked Questions](#17-frequently-asked-questions)
18. [Deprecated and Removed APIs](#deprecated-and-removed-apis)
19. [Glossary](#19-glossary)

---

## 1. Quick Start

The fastest way to get a working player is the free CDN-hosted build. Add the CSS and JavaScript to your page, then mark up a `<video>` element with the `video-js` class and a `data-setup` attribute.

```html
<!DOCTYPE html>
<html>
<head>
  <link href="https://vjs.zencdn.net/8.23.8/video-js.min.css" rel="stylesheet" />
  <script src="https://vjs.zencdn.net/8.23.8/video.min.js"></script>
</head>
<body>
  <video
    id="my-player"
    class="video-js"
    controls
    preload="auto"
    width="640"
    height="360"
    poster="https://vjs.zencdn.net/v/oceans.png"
    data-setup='{}'>
    <source src="https://vjs.zencdn.net/v/oceans.mp4" type="video/mp4" />
    <source src="https://vjs.zencdn.net/v/oceans.webm" type="video/webm" />
    <p class="vjs-no-js">
      To view this video please enable JavaScript, and consider upgrading to a
      web browser that
      <a href="https://videojs.com/html5-video-support/" target="_blank">
        supports HTML5 video
      </a>.
    </p>
  </video>
</body>
</html>
```

When the page loads, Video.js scans for elements with a `data-setup` attribute and automatically turns each one into a player.

> [!TIP]
> The `vjs-no-js` paragraph is a fallback message shown only if JavaScript is disabled. Keep it — it is a built-in accessibility and graceful-degradation feature.

> [!IMPORTANT]
> The `video-js` CSS class is **required** for the player to render and function correctly (fullscreen, captions display, and layout all depend on it). The `data-setup` attribute must contain **valid JSON**; the minimum value is `'{}'`.

---

## 2. Installation

Choose whichever delivery method fits your project. The library is published as the npm package `video.js` and is also available on every major JavaScript CDN.

=== "CDN (Video.js hosted)"

    The Fastly-sponsored CDN at `vjs.zencdn.net` is the simplest option and is what the Quick Start uses.

    ```html
    <link href="https://vjs.zencdn.net/8.23.8/video-js.min.css" rel="stylesheet" />
    <script src="https://vjs.zencdn.net/8.23.8/video.min.js"></script>
    ```

=== "unpkg"

    ```html
    <!-- Always the latest version -->
    <link href="https://unpkg.com/video.js/dist/video-js.min.css" rel="stylesheet" />
    <script src="https://unpkg.com/video.js/dist/video.min.js"></script>

    <!-- Pin a specific version -->
    <link href="https://unpkg.com/video.js@8.23.8/dist/video-js.min.css" rel="stylesheet" />
    <script src="https://unpkg.com/video.js@8.23.8/dist/video.min.js"></script>
    ```

=== "cdnjs"

    ```html
    <link href="https://cdnjs.cloudflare.com/ajax/libs/video.js/8.23.8/video-js.min.css" rel="stylesheet" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/video.js/8.23.8/video.min.js"></script>
    ```

=== "npm"

    ```bash
    npm install --save video.js
    ```

    Then import it in your application code:

    ```js
    import videojs from 'video.js';
    import 'video.js/dist/video-js.css';
    ```

    The package exposes a CommonJS build (`dist/video.cjs.js`), an ES module build (`dist/video.es.js`), the default stylesheet (`dist/video-js.css`), and TypeScript type definitions (`dist/types/video.d.ts`).

> [!NOTE]
> **HLS and DASH work without any extra packages.** The streaming engine ([`@videojs/http-streaming`](https://github.com/videojs/http-streaming), also called "VHS") is bundled into the standard Video.js build. Just point a `<source>` at an `.m3u8` (HLS) or `.mpd` (DASH) URL — see [Working with Sources and Formats](#6-working-with-sources-and-formats).

### Pinning a version

Always pin a specific version in production (e.g. `video.js@8.23.8`) so that a future release of the player cannot change your site's behavior unexpectedly. Use the "latest" URL only for prototypes.

> [!CAUTION]
> The CSS and JavaScript versions must match. Loading `video-js.min.css` from one version and `video.min.js` from another can produce broken layouts or missing icons.

### Self-hosting

If you copy the contents of the package's `dist/` folder onto your own servers, the player works as-is — the fonts and default skin are included. Reference your local copies instead of the CDN URLs.

---

## 3. Setting Up a Player

There are two ways to initialize a player: **automatic setup** via the `data-setup` attribute, and **manual setup** via the `videojs()` function. Use one or the other for a given element — never both.

### Automatic setup

Add a `data-setup` attribute containing options as JSON. Video.js initializes the player when the page is ready.

```html
<video class="video-js" controls data-setup='{ "autoplay": false, "preload": "auto" }'>
  <source src="/media/intro.mp4" type="video/mp4" />
</video>
```

### Manual setup

If your markup is added dynamically (single-page apps, modals, AJAX, `appendChild`), remove `data-setup` and call `videojs()` yourself **after** the element exists in the DOM.

```js
const player = videojs('my-player', {
  controls: true,
  autoplay: false,
  preload: 'auto'
}, function onPlayerReady() {
  // `this` is the player instance.
  videojs.log('Your player is ready!');
});
```

The `videojs()` function takes three arguments:

| Argument | Type | Required | Purpose |
|----------|------|----------|---------|
| `id` | `string` \| `Element` | Yes | The `id` of the media element, or the element itself. A leading `#` is stripped automatically. |
| `options` | `Object` | No | Player options (see [Player Options Reference](#5-player-options-reference)). |
| `ready` | `Function` | No | Callback invoked when the player and its tech are ready. Inside it, `this` is the player. |

You can pass the element directly instead of an `id`:

```js
const el = document.querySelector('.my-video');
const player = videojs(el, { controls: true });
```

> [!WARNING]
> Calling `videojs()` with an `id` that already has a player returns the **existing** player and logs a warning — your new `options` are ignored. To re-create a player with new options, you must [dispose](#14-removing-and-replacing-players) the old one first. To safely fetch an existing player without ever creating one, use `videojs.getPlayer(id)`.

### The `<video-js>` element

Instead of a `<video>` tag you can use a `<video-js>` element. This avoids a brief flash of the unstyled native video controls before Video.js initializes. It is otherwise configured identically.

```html
<video-js id="my-player" class="video-js" controls>
  <source src="/media/intro.mp4" type="video/mp4" />
</video-js>
```

### Waiting until the player is ready

Setup is asynchronous. Run any code that touches the API inside a `ready` callback or via `player.ready()`:

```js
videojs('my-player').ready(function () {
  const player = this;
  player.play();
});
```

---

## 4. Everyday Playback Control (the API)

Once you have a player instance, you control everything through its methods. Video.js mirrors the standard HTML5 media API, with one key difference: **properties are accessed through getter/setter methods**. Call a method with no argument to *get* the value; call it with an argument to *set* it.

```js
// Native HTML5 element
myVideoElement.currentTime = 120;

// Video.js player
myPlayer.currentTime(120);   // set
myPlayer.currentTime();      // get → 120
```

### Playback

| Method | Description |
|--------|-------------|
| `player.play()` | Start (or resume) playback. Returns a Promise on browsers that support it. |
| `player.pause()` | Pause playback. |
| `player.paused()` | `true` if playback is currently paused. |
| `player.ended()` | `true` if playback has reached the end. |
| `player.load()` | Reload the current source. |
| `player.reset()` | Reset the player to its initial state. |

```js
const player = videojs('my-player');
player.play();

player.on('ended', () => {
  videojs.log('Playback finished');
});
```

> [!TIP]
> `player.play()` may return a rejected Promise (for example, when a browser blocks autoplay). Always handle the rejection to avoid unhandled-promise warnings:
> ```js
> const p = player.play();
> if (p !== undefined) {
>   p.catch(() => { /* autoplay was prevented; show a play button */ });
> }
> ```

### Time and seeking

| Method | Description |
|--------|-------------|
| `player.currentTime([seconds])` | Get or set the playback position in seconds. |
| `player.duration([seconds])` | Get the media duration in seconds (`Infinity` for live). |
| `player.remainingTime()` | Seconds left until the end. |
| `player.buffered()` | A `TimeRange` of buffered regions. |
| `player.bufferedPercent()` | Fraction (0–1) of the media that is buffered. |
| `player.bufferedEnd()` | The end time of the buffered range nearest the current time. |
| `player.seekable()` | A `TimeRange` of seekable regions. |
| `player.seeking()` | `true` while a seek is in progress. |
| `player.played()` | A `TimeRange` of regions that have been played. |

```js
// Skip forward 10 seconds
player.currentTime(player.currentTime() + 10);
```

### Volume and muting

| Method | Description |
|--------|-------------|
| `player.volume([decimal])` | Get or set volume as a decimal between `0` and `1`. |
| `player.muted([bool])` | Get or set the muted state. |
| `player.defaultMuted([bool])` | Get or set whether the media starts muted on each load. |

```js
player.volume(0.5);   // 50% volume
player.muted(true);   // mute
```

### Sizing

| Method | Description |
|--------|-------------|
| `player.width([value])` | Get or set the player width (number = pixels, or a `%`/`px` string). |
| `player.height([value])` | Get or set the player height. |
| `player.dimensions(width, height)` | Set both at once. |
| `player.videoWidth()` | The intrinsic width of the loaded video. |
| `player.videoHeight()` | The intrinsic height of the loaded video. |

### Fullscreen and Picture-in-Picture

| Method | Description |
|--------|-------------|
| `player.requestFullscreen([options])` | Enter fullscreen. |
| `player.exitFullscreen()` | Leave fullscreen. |
| `player.isFullscreen([bool])` | Get or set the fullscreen state. |
| `player.supportsFullScreen()` | Whether fullscreen is available in this environment. |
| `player.requestPictureInPicture()` | Enter Picture-in-Picture. |
| `player.exitPictureInPicture()` | Leave Picture-in-Picture. |
| `player.isInPictureInPicture([bool])` | Get or set the PiP state. |

```js
const fsToggle = document.querySelector('#go-fullscreen');
fsToggle.addEventListener('click', () => player.requestFullscreen());
```

### Playback rate

| Method | Description |
|--------|-------------|
| `player.playbackRate([rate])` | Get or set the current speed (e.g. `1.5`). |
| `player.defaultPlaybackRate([rate])` | Get or set the speed used on each new load. |
| `player.playbackRates([rates])` | Get or set the list of selectable speeds shown in the UI menu. |

```js
// Show a speed menu with these options
player.playbackRates([0.5, 1, 1.5, 2]);
```

### Source and metadata

| Method | Description |
|--------|-------------|
| `player.src([source])` | Get the current source, or set a new one (string URL, `{src, type}` object, or array of such objects). |
| `player.currentSrc()` | The URL currently being played. |
| `player.currentType()` | The MIME type currently being played. |
| `player.currentSource()` / `player.currentSources()` | The active source object / all candidate sources. |
| `player.canPlayType(type)` | Whether a given MIME type can be played. |
| `player.poster([url])` | Get or set the poster image. |

```js
player.src({ src: '/media/lecture.m3u8', type: 'application/x-mpegURL' });
```

### State and errors

| Method | Description |
|--------|-------------|
| `player.error([err])` | Get the current `MediaError`, or set/clear an error. |
| `player.readyState()` | The HTML5 media `readyState` value. |
| `player.networkState()` | The HTML5 media `networkState` value. |
| `player.controls([bool])` | Get or set whether the control bar is shown. |
| `player.userActive([bool])` | Get or set user-activity state (drives auto-hiding of controls). |
| `player.hasStarted([bool])` | Whether playback has started at least once. |
| `player.language([code])` | Get or set the player language. |

For the complete list of methods, see the [Player API section of the Admin Guide](videojs__video.js__AdminGuide.md).

---

## 5. Player Options Reference

Options can be supplied three ways, all equivalent:

1. **HTML attributes** on the media element (for standard media attributes like `controls`, `autoplay`, `preload`, `poster`, `loop`, `muted`, `width`, `height`).
2. **The `data-setup` attribute** as a JSON object (for any option, standard or Video.js-specific).
3. **The second argument to `videojs()`** as a JavaScript object.

```html
<!-- Attributes -->
<video class="video-js" controls autoplay preload="auto"></video>

<!-- data-setup JSON -->
<video class="video-js" data-setup='{ "controls": true, "autoplay": false, "preload": "auto" }'></video>
```

```js
// videojs() object
videojs('my-player', { controls: true, autoplay: false, preload: 'auto' });
```

> [!WARNING]
> **Boolean HTML attributes are tricky.** A boolean attribute is "on" simply by being present. Writing `controls="false"` actually turns controls **on**, because the attribute is still there. Use `<video controls>` to enable and omit the attribute entirely to disable. (Inside `data-setup` JSON or the options object, use real booleans: `"controls": false`.)

### Standard media options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `controls` | `boolean` | `false` | Show the player's control bar. Without it, playback can only start via `autoplay` or the API. |
| `autoplay` | `boolean` \| `string` | `false` | Start playing on load. Also accepts the strings `'muted'`, `'play'`, and `'any'` for autoplay strategies (see below). |
| `preload` | `string` | `'auto'` | How much media to load up front: `'auto'`, `'metadata'`, or `'none'`. |
| `poster` | `string` | — | URL of the image shown before playback begins. |
| `loop` | `boolean` | `false` | Restart automatically when playback ends. |
| `muted` | `boolean` | `false` | Start muted. |
| `width` | `number` | — | Display width in pixels. |
| `height` | `number` | — | Display height in pixels. |
| `crossOrigin` | `string` | — | CORS mode for the media and tracks: `'anonymous'` or `'use-credentials'`. (Both `crossOrigin` and `crossorigin` are accepted.) |

> [!NOTE]
> **Autoplay is restricted by browsers.** Apple iOS and most modern browsers block autoplay with sound to protect users' data and attention. A common pattern is `autoplay: 'muted'`, which starts muted (allowed by browsers) so the viewer can unmute. The `normalizeAutoplay` option (below) makes Video.js treat a plain `autoplay: true` like `'play'` for more consistent cross-browser behavior.

### Video.js-specific options

These are the defaults defined in the player itself (Video.js 8.23.8):

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `techOrder` | `string[]` | `['html5']` | Order in which playback technologies are tried. |
| `html5` | `Object` | `{}` | Options passed to the HTML5 tech (e.g. `nativeTextTracks`, `nativeAudioTracks`, `nativeVideoTracks`, VHS options). |
| `enableSourceset` | `boolean` | `true` | Fire the `sourceset` event whenever the underlying media source changes. |
| `inactivityTimeout` | `number` | `2000` | Milliseconds of inactivity before the controls auto-hide. Set to `0` to disable auto-hiding. |
| `playbackRates` | `number[]` | `[]` | Speeds shown in the playback-rate menu. An empty array hides the menu. |
| `liveui` | `boolean` | `false` | Use the enhanced live UI (with a seekable live progress bar and "live" edge button) for live streams. |
| `children` | `string[]` | *(see below)* | Which default child components to build, and their order. |
| `language` | `string` | browser/`'en'` | The player's language code. Defaults to the browser's preferred language, falling back to `'en'`. |
| `languages` | `Object` | `{}` | Per-player translation dictionaries. |
| `notSupportedMessage` | `string` | `'No compatible source was found for this media.'` | Message shown when no source can be played. |
| `normalizeAutoplay` | `boolean` | `false` | Treat `autoplay: true` like `autoplay: 'play'`. |
| `fullscreen` | `Object` | `{ options: { navigationUI: 'hide' } }` | Options passed to the Fullscreen API request. |
| `breakpoints` | `Object` | `{}` | Custom width breakpoints for responsive layout classes (see [Responsive Sizing](#9-responsive-sizing-and-layout)). |
| `responsive` | `boolean` | `false` | Automatically toggle layout classes based on player width. |
| `fluid` | `boolean` | `false` | Scale the player to fill its container's width while keeping aspect ratio. |
| `fill` | `boolean` | `false` | Fill the container's full width and height. |
| `aspectRatio` | `string` | — | Force an aspect ratio like `'16:9'`. Implies `fluid: true`. |
| `audioOnlyMode` | `boolean` | `false` | Hide the big play button, poster, and video display; show only the control bar. |
| `audioPosterMode` | `boolean` | `false` | Show the poster and controls but no video display (for audio-with-artwork). |
| `spatialNavigation` | `Object` | `{ enabled: false, horizontalSeek: false }` | Arrow-key navigation between controls (useful on TVs / D-pad remotes). |
| `enableSmoothSeeking` | `boolean` | `false` | Update the displayed time continuously while dragging the seek bar. |
| `disableSeekWhileScrubbingOnMobile` | `boolean` | `false` | Don't seek the media until the user releases the scrub bar, on mobile. |
| `disableSeekWhileScrubbingOnSTV` | `boolean` | `false` | The same, for smart TVs. |
| `experimentalSvgIcons` | `boolean` | `false` | Use crisp SVG icons instead of the icon font. |
| `disablePictureInPicture` | `boolean` | `false` | Disable the Picture-in-Picture control and capability. |
| `preferFullWindow` | `boolean` | `false` | Use the CSS "full window" fallback instead of the native Fullscreen API. |
| `noUITitleAttributes` | `boolean` | `false` | Don't add `title` tooltips to control buttons. |
| `plugins` | `Object` | — | Plugins to initialize with the player, keyed by name (see [Plugins](#13-plugins)). |

The default `children` are, in order: `mediaLoader`, `posterImage`, `titleBar`, `textTrackDisplay`, `loadingSpinner`, `bigPlayButton`, `liveTracker`, `controlBar`, `errorDisplay`, `textTrackSettings`, `resizeManager`.

### Component options

Any default component can be configured — or removed — by name. For example, to remove the mute toggle (a child of the control bar):

```js
videojs('my-player', {
  controlBar: {
    muteToggle: false
  }
});
```

Setting a component option to `false` disables that component. The same works in `data-setup` JSON:

```html
<video class="video-js" data-setup='{ "controlBar": { "muteToggle": false } }'></video>
```

See [Components: Customizing the UI](#12-components-customizing-the-ui) for the full component tree.

---

## 6. Working with Sources and Formats

Video.js plays any format the browser supports natively, plus adaptive streaming formats via its bundled streaming engine.

### Supported formats

| Format | Type string | How it plays |
|--------|-------------|--------------|
| MP4 (H.264) | `video/mp4` | Native browser playback. |
| WebM | `video/webm` | Native browser playback. |
| Ogg | `video/ogg` | Native browser playback. |
| **HLS** (`.m3u8`) | `application/x-mpegURL` | Bundled streaming engine, or native on Safari. |
| **MPEG-DASH** (`.mpd`) | `application/dash+xml` | Bundled streaming engine. |
| MP3 audio | `audio/mp3` | Native browser playback. |

### Providing multiple sources

List several `<source>` elements and Video.js picks the first one the current browser can play:

```html
<video class="video-js" controls>
  <source src="/media/intro.m3u8" type="application/x-mpegURL" />
  <source src="/media/intro.mp4" type="video/mp4" />
  <source src="/media/intro.webm" type="video/webm" />
</video>
```

> [!IMPORTANT]
> Always include the `type` attribute on every `<source>`. Without it, the player must guess from the file extension, and sources may be skipped or chosen incorrectly. By default the player uses **tech-first** selection: it tries each source against the first tech in `techOrder` before moving on.

### Changing the source from JavaScript

```js
// Single source (let Video.js infer the type if omitted)
player.src('/media/lecture.mp4');

// Source with explicit type (recommended)
player.src({ src: '/media/lecture.m3u8', type: 'application/x-mpegURL' });

// Multiple candidates
player.src([
  { src: '/media/lecture.m3u8', type: 'application/x-mpegURL' },
  { src: '/media/lecture.mp4', type: 'video/mp4' }
]);
```

### Cross-origin media

If your media or tracks are served from a different domain than the page, enable CORS on the server **and** set the `crossorigin` attribute:

```html
<video class="video-js" crossorigin="anonymous" controls>
  <source src="https://cdn.example.com/v/oceans.mp4" type="video/mp4" />
  <track src="https://cdn.example.com/v/oceans.vtt" kind="captions" srclang="en" label="English" />
</video>
```

> [!CAUTION]
> Most browsers apply `crossorigin` to the video source itself, not just to text tracks. This means the **video files** also need the appropriate CORS headers, or playback may fail. Use `anonymous` unless you specifically need credentials.

---

## 7. Captions, Subtitles, and Other Text Tracks

Text tracks provide time-aligned text — captions, subtitles, chapters, descriptions, and machine-readable metadata. Video.js makes them work consistently across browsers using WebVTT files.

### The five kinds of text track

| Kind | Purpose |
|------|---------|
| `subtitles` | Translations of dialogue, shown over the video. The default if `kind` is omitted. |
| `captions` | Dialogue **plus** sound effects and cues, for deaf/hard-of-hearing viewers or muted playback. |
| `chapters` | Named navigation points the viewer can jump to. |
| `descriptions` | Spoken descriptions of on-screen action for blind/low-vision viewers; read by a screen reader. |
| `metadata` | Data for your JavaScript to consume; never shown to the viewer. |

### Adding tracks in HTML

Place `<track>` elements after all `<source>` elements:

```html
<video class="video-js" controls>
  <source src="https://vjs.zencdn.net/v/oceans.mp4" type="video/mp4" />
  <track kind="captions" src="/tracks/oceans-en.vtt" srclang="en" label="English" default />
  <track kind="subtitles" src="/tracks/oceans-es.vtt" srclang="es" label="Español" />
</video>
```

| Track attribute | Description |
|-----------------|-------------|
| `kind` | One of the five kinds above. Defaults to `subtitles`. |
| `src` | URL of the WebVTT file. |
| `srclang` | BCP 47 language code (e.g. `en`, `es`, `zh-CN`). |
| `label` | Human-readable name shown in the menu. |
| `default` | Show this track by default. **Required for chapters** if you want the chapters menu to appear. |

### Adding and controlling tracks from JavaScript

```js
// Add a removable track
const trackEl = player.addRemoteTextTrack({
  kind: 'captions',
  src: '/tracks/oceans-fr.vtt',
  srclang: 'fr',
  label: 'Français'
}, false);

// Turn a track on
const tracks = player.textTracks();
for (let i = 0; i < tracks.length; i++) {
  const track = tracks[i];
  if (track.kind === 'captions' && track.language === 'en') {
    track.mode = 'showing';
  }
}
```

A track's `mode` is one of:

- `disabled` — not active and not updating.
- `hidden` — updating (events fire) but not visible. Useful for `metadata` tracks.
- `showing` — visible to the viewer.

### Text-track API

| Method | Returns | Description |
|--------|---------|-------------|
| `player.textTracks()` | `TextTrackList` | All text tracks on the player. |
| `player.remoteTextTracks()` | `TextTrackList` | Tracks added via `<track>` or `addRemoteTextTrack` (all removable). |
| `player.remoteTextTrackEls()` | `HTMLTrackElementList` | The underlying track elements. |
| `player.addTextTrack(kind, label, language)` | `TextTrack` | Create a track programmatically. **Cannot be removed** — discouraged. |
| `player.addRemoteTextTrack(options, manualCleanup)` | `HTMLTrackElement` | Create a removable track. |
| `player.removeRemoteTextTrack(track)` | — | Remove a track created with `addRemoteTextTrack`. |

> [!TIP]
> Listen for the `cuechange` event on a `hidden` metadata track to trigger custom behavior when a cue becomes active:
> ```js
> metadataTrack.addEventListener('cuechange', () => {
>   const active = metadataTrack.activeCues[0];
>   if (active) console.log('Now at cue:', active.text);
> });
> ```

### Caption settings

When using emulated text tracks, a "captions settings" item lets viewers restyle captions (font, color, background). To remove it:

```js
videojs('my-player', { textTrackSettings: false });
```

```css
/* Also hide the menu item */
.vjs-texttrack-settings { display: none; }
```

---

## 8. Audio and Video Tracks

Beyond text tracks, HTML5 media supports alternative **audio tracks** (e.g. languages, commentary) and alternative **video tracks** (e.g. camera angles). Video.js exposes both through list objects.

### Audio tracks

Audio tracks must currently be added **programmatically** — there is no HTML markup for them.

```js
const tracks = player.audioTracks();

tracks.addEventListener('change', () => {
  for (let i = 0; i < tracks.length; i++) {
    if (tracks[i].enabled) {
      console.log('Now playing audio track:', tracks[i].label);
    }
  }
});
```

| Property | Description |
|----------|-------------|
| `kind` | `alternative`, `descriptions`, `main`, `translation`, or `commentary`. |
| `label` | Name shown in the menu. |
| `language` | BCP 47 language code. |
| `enabled` | Whether this track is the active one. Only one track is enabled at a time. |

| Method | Description |
|--------|-------------|
| `player.audioTracks()` | The `AudioTrackList`. |
| `player.audioTracks().addTrack(track)` | Add a track. |
| `player.audioTracks().removeTrack(track)` | Remove a track. |

> [!NOTE]
> Video.js stores the audio-track *representation* and surfaces a selection menu, but the actual decode-time switching of audio is handled by the playback engine (for adaptive streams, the bundled streaming engine handles it). Progressive files with multiple audio tracks depend on browser support.

### Video tracks

Video tracks (alternative camera angles) follow the same pattern via `player.videoTracks()`, returning a `VideoTrackList`.

---

## 9. Responsive Sizing and Layout

By default a player has a fixed pixel size. For sites that need the player to scale with the page, Video.js offers fluid, fill, and responsive modes.

### Fixed size

```html
<video class="video-js" width="640" height="360"></video>
```

### Fluid mode

Fluid mode makes the player fill its container's **width** and compute its height from the aspect ratio.

```js
videojs('my-player', { fluid: true });
```

Or add the `vjs-fluid` class to the element. To force a particular ratio:

```js
videojs('my-player', { aspectRatio: '16:9' });   // implies fluid
```

> [!NOTE]
> If no aspect ratio is set, the player uses the loaded video's intrinsic dimensions, falling back to `16:9` when those aren't yet known.

### Fill mode

Fill mode makes the player occupy 100% of its container's width **and** height — useful when the container itself controls the size.

```js
videojs('my-player', { fill: true });
```

> [!IMPORTANT]
> `fluid` and `fill` are mutually exclusive. Turning one on automatically turns the other off.

### Responsive layout classes

With `responsive: true`, the player adds a layout class based on its current width, letting the skin show/hide controls at different sizes. The default breakpoints are:

| Breakpoint | Max width (px) | Layout class |
|------------|----------------|--------------|
| `tiny` | 210 | `vjs-layout-tiny` |
| `xsmall` | 320 | `vjs-layout-x-small` |
| `small` | 425 | `vjs-layout-small` |
| `medium` | 768 | `vjs-layout-medium` |
| `large` | 1440 | `vjs-layout-large` |
| `xlarge` | 2560 | `vjs-layout-x-large` |
| `huge` | `Infinity` | `vjs-layout-huge` |

Override any breakpoint with the `breakpoints` option:

```js
videojs('my-player', {
  responsive: true,
  breakpoints: { medium: 800 }
});
```

| Method | Description |
|--------|-------------|
| `player.fluid([bool])` | Get/set fluid mode. |
| `player.fill([bool])` | Get/set fill mode. |
| `player.aspectRatio([ratio])` | Get/set the aspect ratio. |
| `player.responsive([bool])` | Get/set responsive mode. |
| `player.breakpoints([obj])` | Get/set breakpoints. |
| `player.currentBreakpoint()` | The active breakpoint name. |

---

## 10. Languages and Localization

Video.js ships with translations for many languages and lets you add your own. Control text (button labels, error messages) is localized through key/value JSON dictionaries.

### Setting the language

Pass the `language` option, or call `player.language()`:

```js
videojs('my-player', { language: 'es' });
```

The player resolves its language in this order of priority:

1. The `language` option.
2. The `lang` attribute of the nearest ancestor element (often the `<html>` tag).
3. The browser's preferred language.
4. `'en'`.

Language codes are matched case-insensitively, and a subcode like `en-US` falls back to the primary code `en` if no exact match exists.

> [!NOTE]
> Calling `player.language('fr')` after creation switches the language, but already-rendered text is not re-translated. For best results, set the language before the player initializes.

### Adding a language globally

Use `videojs.addLanguage()` before any player is created — ideally in the page `<head>`:

```js
videojs.addLanguage('es', {
  'Play': 'Reproducción',
  'Pause': 'Pausa',
  'Current Time': 'Tiempo reproducido',
  'Duration': 'Duración total',
  'Mute': 'Silenciar',
  'Fullscreen': 'Pantalla completa'
});
```

### Per-player translations

```js
videojs('my-player', {
  language: 'es',
  languages: {
    es: { 'Play': 'Juego' }
  }
});
```

Per-player values override the global dictionary for that player only.

---

## 11. Skinning and Styling

The default skin is plain HTML and CSS, so you can restyle the player by overriding CSS classes — no build step required.

### Customizing the default skin

Add a class to your element and override the base styles:

```css
.vjs-skin-sunset .vjs-control-bar { background: #2b1055; }
.vjs-skin-sunset .vjs-play-progress { background: #ff7e5f; }
.vjs-skin-sunset { color: #ffd9b3; }
```

```html
<video class="video-js vjs-skin-sunset" controls></video>
```

> [!TIP]
> Use your browser's developer tools to inspect player elements and discover the class names to target. Every control has a `vjs-`-prefixed class.

### Centering the big play button

By default the big play button sits in the top-left so it doesn't obscure the poster. Add `vjs-big-play-centered` to center it:

```html
<video class="video-js vjs-big-play-centered" controls></video>
```

### Disabling injected styles

Video.js injects two `<style>` elements at runtime: the base default styles and the per-player dimension styles. You can disable each with a global flag set **before** Video.js loads:

| Global flag | Effect |
|-------------|--------|
| `window.VIDEOJS_NO_DYNAMIC_STYLE = true` | Don't inject the default-dimensions and player-dimension style elements. You must then size the player yourself. |
| `window.VIDEOJS_NO_BASE_THEME = true` | Don't inject the base skin styles. You must include them manually. |

> [!WARNING]
> With `VIDEOJS_NO_DYNAMIC_STYLE = true`, a player given `width="600" height="300"` will end up 0×0 unless you set dimensions another way — Video.js applies the width/height directly to the underlying media element in that mode. This flag exists mainly for environments (like some React setups) where the `<head>` is managed externally.

### SVG icons

Set `experimentalSvgIcons: true` to render crisp, scalable SVG icons instead of the icon font:

```js
videojs('my-player', { experimentalSvgIcons: true });
```

---

## 12. Components: Customizing the UI

Every visible piece of a Video.js player — the control bar, each button, the progress bar — is a **Component**. The player itself is a component, and components can contain child components. This lets you add, remove, rearrange, or replace any part of the UI.

### The default component tree

```text
Player
├── PosterImage
├── TextTrackDisplay
├── LoadingSpinner
├── BigPlayButton
├── TitleBar
├── ControlBar
│   ├── PlayToggle
│   ├── VolumePanel (MuteToggle + VolumeControl)
│   ├── CurrentTimeDisplay      (hidden by default)
│   ├── TimeDivider             (hidden by default)
│   ├── DurationDisplay         (hidden by default)
│   ├── ProgressControl
│   │   └── SeekBar
│   │       ├── LoadProgressBar
│   │       ├── MouseTimeDisplay
│   │       └── PlayProgressBar
│   ├── LiveDisplay             (hidden by default)
│   ├── RemainingTimeDisplay
│   ├── CustomControlSpacer
│   ├── ChaptersButton          (hidden by default)
│   ├── DescriptionsButton      (hidden by default)
│   ├── SubtitlesButton         (hidden by default)
│   ├── CaptionsButton          (hidden by default)
│   ├── AudioTrackButton        (hidden by default)
│   ├── PictureInPictureToggle
│   └── FullscreenToggle
├── ErrorDisplay
└── TextTrackSettings
```

### Adding a child component

```js
const player = videojs('my-player');
player.addChild('BigPlayButton');
```

Adding a child inserts its DOM element into the parent's element.

### Configuring and removing components

Pass a config object — or `false` to remove — keyed by component name:

```js
videojs('my-player', {
  controlBar: {
    pictureInPictureToggle: false,        // remove PiP button
    progressControl: {
      keepTooltipsInside: true            // configure the progress control
    }
  }
});
```

### Component methods you'll commonly use

| Method | Description |
|--------|-------------|
| `component.show()` / `component.hide()` | Toggle visibility (`vjs-hidden` class). |
| `component.addClass(name)` / `removeClass(name)` / `toggleClass(name)` | Manage CSS classes. |
| `component.el()` | The component's DOM element. |
| `component.getChild(name)` | A child component by name. |
| `component.on(type, fn)` / `off` / `one` / `trigger` | Event handling. |
| `player.getChild('controlBar').getChild('playToggle')` | Walk the component tree. |

---

## 13. Plugins

Plugins package reusable behavior. Video.js has a large [plugin ecosystem](https://videojs.com/plugins/), and writing your own is straightforward.

### Using a plugin

If you create the player dynamically, list plugins in the `plugins` option:

```js
videojs('my-player', {
  plugins: {
    examplePlugin: { exampleOption: true }
  }
});
```

If the player already exists, call the plugin's method directly:

```js
const player = videojs('my-player');
player.examplePlugin({ exampleOption: true });
```

### Writing a basic plugin

A basic plugin is a function. Inside it, `this` is the player. Register it with `videojs.registerPlugin()`:

```js
function examplePlugin(options) {
  this.on('play', () => {
    videojs.log('Playback started!');
  });
}

videojs.registerPlugin('examplePlugin', examplePlugin);
```

Once registered, the plugin is available as a method on every player instance.

> [!IMPORTANT]
> Choose a unique plugin name that doesn't collide with an existing player property or method. The older `videojs.plugin()` registration function is **deprecated** and will be removed in v9.0 — use `videojs.registerPlugin()`.

### Advanced plugins

For stateful plugins, extend the base `Plugin` class (retrieved via `videojs.getPlugin('plugin')`), which gives you events, state, and automatic cleanup on player dispose:

```js
const Plugin = videojs.getPlugin('plugin');

class AnalyticsPlugin extends Plugin {
  constructor(player, options) {
    super(player);
    this.on(player, 'play', () => this.report('play'));
  }
  report(event) { /* ... */ }
}

videojs.registerPlugin('analytics', AnalyticsPlugin);
```

### Plugin registry helpers

| Function | Description |
|----------|-------------|
| `videojs.registerPlugin(name, plugin)` | Register a plugin. |
| `videojs.deregisterPlugin(name)` | Remove a registration. |
| `videojs.getPlugin(name)` | Get a registered plugin. |
| `videojs.getPlugins()` | Get all registered plugins. |
| `videojs.getPluginVersion(name)` | Get a plugin's version, if it set one. |
| `player.hasPlugin(name)` | Whether a plugin is registered. |
| `player.usingPlugin(name)` | Whether a plugin is active on this player. |

---

## 14. Removing and Replacing Players

When a player is no longer needed — a modal closes, a route changes, a component unmounts — **dispose** it. This frees resources and clears Video.js's internal tracking.

```js
const player = videojs('my-player');
player.dispose();
```

Disposing:

- Resets Video.js's internal state for that player.
- Removes the player's DOM from the page.

> [!IMPORTANT]
> Video.js tracks every player by its element `id`. If you create a new player with the same `id` as a previous one without disposing the old one first, you will get errors. Always `dispose()` before re-using an `id`.

### In single-page apps and modals

Create the player when the container appears, and dispose it when the container is hidden or unmounted:

```js
// React example
useEffect(() => {
  const player = videojs(videoRef.current, options);
  return () => player.dispose();   // cleanup on unmount
}, []);
```

> [!WARNING]
> Hiding a player with CSS instead of disposing it leaves it consuming resources and can cause errors with streaming playback. Dispose, don't just hide.

---

## 15. Events Reference

Listen for events with `player.on(type, handler)` (and remove with `player.off`, listen once with `player.one`). Handlers receive the event object.

```js
player.on('timeupdate', () => {
  document.querySelector('#clock').textContent = player.currentTime().toFixed(1);
});
```

### Lifecycle and readiness

| Event | Fires when… |
|-------|-------------|
| `ready` | The player and tech are ready. (Prefer `player.ready()`.) |
| `loadstart` | The browser begins loading media. |
| `loadedmetadata` | Duration and dimensions are known. |
| `loadeddata` | The first frame is loaded. |
| `dispose` | The player is being disposed. |

### Playback

| Event | Fires when… |
|-------|-------------|
| `play` | Playback starts or resumes. |
| `pause` | Playback pauses. |
| `playing` | Playback actually begins after buffering. |
| `ended` | Playback reaches the end. |
| `timeupdate` | The position changes (every ~15–250 ms during playback). |
| `seeking` / `seeked` | A seek starts / completes. |
| `ratechange` | The playback rate changes. |
| `waiting` | Playback stalls waiting for data. |

### Media state

| Event | Fires when… |
|-------|-------------|
| `progress` | More media data has downloaded. |
| `durationchange` | The duration changes. |
| `volumechange` | Volume or muted state changes. |
| `resize` | The video's intrinsic dimensions change. |
| `error` | A playback error occurs. |
| `fullscreenchange` | Fullscreen is entered or exited. |
| `enterpictureinpicture` / `leavepictureinpicture` | PiP state changes. |
| `texttrackchange` | A text track changes. |
| `languagechange` | The player language changes. |
| `sourceset` | The underlying media source is set (when `enableSourceset` is on). |

---

## 16. Troubleshooting

Each entry lists the error or symptom, its cause, the fix, and how to prevent it.

### Error: `The element or ID supplied is not valid. (videojs)`

- **Cause:** You called `videojs()` with an `id` that matches no element, or with something that isn't a DOM element.
- **Solution:** Verify the element exists in the DOM **before** calling `videojs()`. For dynamically added markup, call `videojs()` only after insertion.
- **Prevention:** Use manual setup for dynamic content; confirm the `id` matches your markup exactly.

### Warning: `Player "<id>" is already initialised. Options will not be applied.`

- **Cause:** `videojs()` was called for an element that already has a player; the existing player is returned and your new options are ignored.
- **Solution:** To change options, `dispose()` the existing player and create a new one, or use the relevant API method (`player.src()`, `player.controls()`, etc.) to change settings at runtime.
- **Prevention:** Use `videojs.getPlayer(id)` to fetch a player without re-initializing.

### Warning: `The element supplied is not included in the DOM`

- **Cause:** The element passed to `videojs()` is not attached to the document (or its document has no default view).
- **Solution:** Insert the element into the DOM before initializing.
- **Prevention:** In SPAs, initialize inside the lifecycle hook that runs after mount.

### Error: `No compatible source was found for this media.`

- **Cause:** None of the provided `<source>` URLs can be played by the browser/tech — wrong format, missing `type`, or a server delivering the wrong MIME type.
- **Solution:** Provide a widely supported fallback such as MP4 (H.264), and add correct `type` attributes. Verify the server's `Content-Type` header.
- **Prevention:** Offer multiple sources with explicit `type` values; test in your target browsers.

### Error: `TypeError: this.el_.vjs_getProperty is not a function`

- **Cause:** A player was removed from the DOM without calling `dispose()` first (common in modals and SPAs).
- **Solution:** Always call `player.dispose()` before removing a player's markup.
- **Prevention:** Pair every `videojs()` with a `dispose()` in your cleanup path.

### Error: `Improper value supplied for aspect ratio. The format should be width:height, for example 16:9.`

- **Cause:** The `aspectRatio` option or `player.aspectRatio()` was given a malformed value.
- **Solution:** Use the `width:height` form, e.g. `'16:9'` or `'4:3'`.
- **Prevention:** Validate user-supplied ratios before passing them in.

### Symptom: Autoplay doesn't work

- **Cause:** The browser blocks autoplay with sound (especially iOS).
- **Solution:** Use `autoplay: 'muted'` so playback starts muted, and provide an unmute control. Consider `normalizeAutoplay: true`.
- **Prevention:** Never rely on unmuted autoplay; design for a user gesture to start audio.

### Symptom: Player renders at 0×0 or unstyled

- **Cause:** Missing the `video-js` class, the CSS not loaded, or `VIDEOJS_NO_DYNAMIC_STYLE` set without sizing the player.
- **Solution:** Ensure the `video-js` class is present, the stylesheet matches the JS version, and dimensions are set.
- **Prevention:** Load matching CSS/JS versions; keep the required class.

### Symptom: Captions or media fail to load from another domain

- **Cause:** Missing CORS headers and/or the `crossorigin` attribute.
- **Solution:** Enable CORS on the server for both the media and the track files, and add `crossorigin="anonymous"` to the element.
- **Prevention:** Serve media and tracks with correct CORS headers from the start.

### Symptom: A control button shows but does nothing / a child can't be added

- **Cause:** Trying to add an interactive child to a `Button` (only `ClickableComponent` children are supported), or referencing a component name that isn't registered.
- **Solution:** Add interactive children to a `ClickableComponent`, and register custom components with `videojs.registerComponent()` before use.
- **Prevention:** Check the component name and base class.

---

## 17. Frequently Asked Questions

**Q: Do I need a separate plugin for HLS or DASH?**
No. The streaming engine (`@videojs/http-streaming`) is bundled into the standard build. Point a `<source>` at an `.m3u8` or `.mpd` URL with the right `type` and it plays.

**Q: Does Video.js still support Flash?**
No. Flash support was removed in Video.js 8. The default `techOrder` is `['html5']`. Old documentation that mentions a `flash` tech, `.swf` files, or `techOrder: ['html5', 'flash']` does not apply to v8.

**Q: How do I get a reference to a player that already exists?**
Use `videojs.getPlayer('my-player')`. Unlike calling `videojs('my-player')`, it never creates a new player. To list all players, use `videojs.getPlayers()` or `videojs.getAllPlayers()`.

**Q: Can I have more than one player on a page?**
Yes. Give each media element a unique `id` and initialize each one. Players are tracked by `id`.

**Q: How do I autoplay without sound?**
Set `autoplay: 'muted'` (or add both the `autoplay` and `muted` attributes). Browsers allow muted autoplay.

**Q: How do I change the video after the player is created?**
Call `player.src(newSource)`. Pass a string, a `{src, type}` object, or an array of candidates.

**Q: How do I make the player resize with the page?**
Use `fluid: true` (scales to container width, keeps ratio), `fill: true` (fills the container), or set an `aspectRatio`. Add `responsive: true` to also toggle layout classes by width.

**Q: How do I remove the player cleanly?**
Call `player.dispose()`. Never just hide it with CSS — that leaks resources and can cause errors.

**Q: How do I add a speed (playback rate) menu?**
Set `playbackRates`, e.g. `{ playbackRates: [0.5, 1, 1.5, 2] }`. An empty array (the default) hides the menu.

**Q: How do I turn captions on by default?**
Add the `default` attribute to the `<track>` element, or set the track's `mode` to `'showing'` in JavaScript.

**Q: How do I localize button labels and messages?**
Set the `language` option and register translations with `videojs.addLanguage()` (global) or the `languages` option (per player).

**Q: Why does `controls="false"` still show controls?**
Because boolean HTML attributes are "on" simply by being present. Omit the attribute to disable controls, or use `"controls": false` in JSON/options.

**Q: How do I disable Picture-in-Picture?**
Set `disablePictureInPicture: true`, or remove just the button with `controlBar: { pictureInPictureToggle: false }`.

**Q: Can I customize or remove parts of the control bar?**
Yes. Every UI element is a component. Set its option to `false` to remove it or to an object to configure it — see [Components](#12-components-customizing-the-ui).

---

## Deprecated and Removed APIs

The following helpers still work in Video.js 8 but log a deprecation warning and **will be removed in 9.0**. Migrate to the listed replacements.

| Deprecated | Use instead |
|------------|-------------|
| `videojs.plugin(name, fn)` | `videojs.registerPlugin(name, fn)` |
| `videojs.mergeOptions(...)` | `videojs.obj.merge(...)` |
| `videojs.defineLazyProperty(...)` | `videojs.obj.defineLazyProperty(...)` |
| `videojs.bind(...)` | native `Function.prototype.bind` |
| `videojs.createTimeRange(...)` / `videojs.createTimeRanges(...)` | `videojs.time.createTimeRanges(...)` |
| `videojs.formatTime(...)` | `videojs.time.formatTime(...)` |
| `videojs.setFormatTime(...)` / `videojs.resetFormatTime(...)` | `videojs.time.setFormatTime(...)` / `videojs.time.resetFormatTime(...)` |
| `videojs.parseUrl(...)` | `videojs.url.parseUrl(...)` |
| `videojs.isCrossOrigin(...)` | `videojs.url.isCrossOrigin(...)` |
| `videojs.computedStyle(...)` | `videojs.dom.computedStyle(...)` |
| `videojs.createEl/addClass/removeClass/...` (DOM helpers on the namespace) | the same functions on `videojs.dom.*` |

**Already removed in v8 (not available):** the Flash playback tech and `.swf` support, and IE8 shims. There is no `flash` entry in `techOrder`.

---

## 19. Glossary

**Tech (Playback Technology)**
: The mechanism that actually decodes and plays the media. In Video.js 8 the tech is HTML5 (the browser's `<video>`/`<audio>` element). The streaming engine plugs into the HTML5 tech to play HLS and DASH.

**Component**
: A reusable UI object that pairs a JavaScript object with a DOM element. The player and every control are components; components can have child components.

**Source**
: A single media file or stream, described by a URL and a MIME `type`. A player can be given several candidate sources.

**Text track**
: Time-aligned text (captions, subtitles, chapters, descriptions, or metadata), supplied as a WebVTT file.

**WebVTT**
: The text format for captions and subtitles — a list of timed "cues," each with a start time, end time, and text.

**TimeRange**
: An array-like object describing time spans (e.g. buffered or seekable regions), with `start(i)` and `end(i)` accessors.

**Fluid mode**
: A sizing mode where the player fills its container's width and computes height from the aspect ratio.

**Skin**
: The visual design of the player's controls, defined entirely in CSS and customizable by overriding `vjs-`-prefixed classes.

**Plugin**
: Packaged, reusable functionality registered with `videojs.registerPlugin()` and attached to players by name.

**CDN (Content Delivery Network)**
: A geographically distributed set of servers that deliver files from the location nearest each user, speeding up load times.

---

_Generated June 2026 from videojs/video.js at 69ea0e0. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
