Jesse A. Green / notebook

Video.js User Guide

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.

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. 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.


Table of Contents

  1. Quick Start
  2. Installation
  3. Setting Up a Player
  4. Everyday Playback Control (the API)
  5. Player Options Reference
  6. Working with Sources and Formats
  7. Captions, Subtitles, and Other Text Tracks
  8. Audio and Video Tracks
  9. Responsive Sizing and Layout
  10. Languages and Localization
  11. Skinning and Styling
  12. Components: Customizing the UI
  13. Plugins
  14. Removing and Replacing Players
  15. Events Reference
  16. Troubleshooting
  17. Frequently Asked Questions
  18. Deprecated and Removed APIs
  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.

<!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.

<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

<!-- 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

<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

npm install --save video.js

Then import it in your application code:

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, 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.

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.

<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.

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).
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:

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 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.

<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():

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.

// 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.
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:

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.
// 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.
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.
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.
// 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.
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.


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.
<!-- 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>
// 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).
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).

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):

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

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

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

See 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:

<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

// 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:

<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:

<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

// 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:

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:

videojs('my-player', { textTrackSettings: false });
/* 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.

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

<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.

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

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

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.

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:

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():

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>:

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

Per-player translations

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:

.vjs-skin-sunset .vjs-control-bar { background: #2b1055; }
.vjs-skin-sunset .vjs-play-progress { background: #ff7e5f; }
.vjs-skin-sunset { color: #ffd9b3; }
<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:

<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:

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

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

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:

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, and writing your own is straightforward.

Using a plugin

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

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

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

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():

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:

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.

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:

// 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.

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.


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.