The native HTML5 <video> element looks different in every browser, ships with controls you can barely style, and leaves adaptive streaming up to you. That is the gap Video.js fills. It sits on top of the browser’s video element and gives you one consistent player, one JavaScript API, and one set of controls that behave the same in Chrome, Safari, Firefox, and on mobile.
Video.js is one of the most widely used open source video players on the web, running on hundreds of thousands of sites after more than 15 years of development. If you are adding playback to a website or app and you want full control over the look, the behavior, and the streaming format, it is usually the first library developers reach for. This guide covers what Video.js is, how it works, how to install it, how it handles HLS and DASH, its plugin and framework ecosystem, and where it fits in a production video stack.
What Is Video.js?
Video.js is an open source HTML5 video player built as a JavaScript and CSS library that wraps the browser’s native <video> element to deliver a consistent playback experience across every browser and device. It is free, MIT licensed, and maintained on the Video.js GitHub repo by a large community of contributors, with full docs on the official Video.js site.
Instead of replacing the browser’s video engine, Video.js builds on top of it. The browser still decodes and renders the video. Video.js standardizes everything around that: the control bar, the styling, the API, captions, fullscreen behavior, and support for streaming formats the native element does not handle on its own.
Here is what you get out of the box:
- A control bar (play/pause, volume, progress bar, fullscreen, playback rate) styled the same everywhere
- A consistent JavaScript API for controlling the player and listening to events
- Support for adaptive streaming formats like HLS and DASH
- Caption and subtitle support (WebVTT)
- A plugin system and themeable skins
- Responsive and fluid layout options
| Attribute | Native HTML5 <video> |
Video.js |
|---|---|---|
| Controls appearance | Different in every browser | Consistent skin everywhere |
| Styling | Very limited | Fully customizable with CSS |
| HLS/DASH support | Safari only (HLS native) | HLS and DASH on all browsers |
| JavaScript API | Basic, inconsistent | Rich, consistent API + events |
| Plugins | None | Large plugin ecosystem |
| License / cost | Built in | Free, open source (MIT) |
The current stable release is the version 8.x line (served from the CDN as builds like 8.23.9), and the project has a version 10 beta rebuilt for modern frameworks. For most production sites today, the 8.x branch is the one you will use.
Video.js vs Native HTML5 Video vs Other Players
Before you commit to a library, it helps to know where Video.js sits relative to the alternatives. The <video> tag is built into the browser, but it is not enough on its own for most real projects. Other libraries solve narrower problems.
- Native
<video>element: Zero dependencies, but inconsistent controls, no cross-browser HLS, and painful styling. - Video.js: A full player UI plus streaming support plus plugins. The most complete general purpose option.
- hls.js: A playback engine that adds HLS to browsers using Media Source Extensions. It handles the stream, not the UI. Video.js actually uses a similar engine under the hood.
- Shaka Player: Google’s player, strong on DASH and DRM. More focused on premium content protection than on a customizable UI.
The short version: reach for Video.js when you want a ready made, skinnable player UI that also handles adaptive streaming. Reach for hls.js or Shaka when you are building your own UI and only need the streaming engine. If you are weighing the two delivery formats themselves, our guide on HLS vs DASH breaks down the trade offs.
How Does Video.js Work?
Video.js follows a clear lifecycle from the moment the page loads to the moment playback starts. Understanding it makes debugging and customization much easier.
- Load the library. You include the Video.js CSS and JavaScript, either from a CDN or a bundled package. This registers a global
videojsfunction. - Mark up a video element. You write a standard
<video>tag with thevideo-jsclass and adata-setupattribute, or you create the element in JavaScript. - Initialize the player. Video.js finds the element (automatically via
data-setup, or manually when you callvideojs()) and replaces the native controls with its own component tree. - Pick a playback tech. Video.js selects how to play the source. For a plain MP4 it uses the native element. For an HLS or DASH stream it uses its built in HTTP streaming engine.
- Render components. The control bar, big play button, progress bar, and any plugins mount as a tree of components you can add to, remove, or restyle.
- Emit events. The player fires events like
play,pause,timeupdate,ended, anderrorthat your code can listen to.
The component architecture is the part worth internalizing. Every visible piece of the player is a component in a tree, and each one exposes methods and events. That is why you can move the volume control, add a custom button, or build an entire plugin without forking the library.
How to Install and Use Video.js
There are two common ways to add Video.js to a project: a CDN include for quick setups and static sites, or an npm install for apps with a build step. Both take only a few minutes.
Option 1: Add Video.js from a CDN
The fastest path is the hosted build. Add the stylesheet and script to your page, then create a video element with the video-js class and a data-setup attribute. Video.js initializes it automatically.
<link href="https://vjs.zencdn.net/8.23.9/video-js.css" rel="stylesheet" />
<video
id="my-player"
class="video-js"
controls
preload="auto"
width="640"
height="360"
poster="poster.jpg"
data-setup='{}'>
<source src="video.mp4" type="video/mp4" />
<p class="vjs-no-js">
To view this video please enable JavaScript.
</p>
</video>
<script src="https://vjs.zencdn.net/8.23.9/video.min.js"></script>
The data-setup='{}' attribute tells Video.js to initialize the player. You can pass options as JSON inside those braces, such as data-setup='{"fluid": true}' for responsive sizing.
Option 2: Install Video.js with npm
For a project with a bundler, install the package and import it. This is the right approach for single page apps and anything with a build pipeline.
npm install video.js
import videojs from 'video.js';
import 'video.js/dist/video-js.css';
const player = videojs('my-player', {
controls: true,
autoplay: false,
preload: 'auto',
fluid: true,
sources: [{
src: 'https://example.com/video.mp4',
type: 'video/mp4'
}]
});
player.ready(function () {
console.log('Player is ready');
});
Working with the Video.js API
Once you have a player instance, the Video.js API lets you control everything programmatically. A few of the most common calls:
player.play(); // start playback
player.pause(); // pause
player.currentTime(30); // seek to 30 seconds
player.volume(0.5); // set volume to 50%
player.src({ src: 'new.m3u8', type: 'application/x-mpegURL' }); // swap source
player.on('ended', function () {
console.log('Video finished');
});
The same event driven pattern applies whether you are building analytics, custom overlays, or ad triggers. If you are combining playback with a broader build, our video API developer guide walks through how the frontend player and backend infrastructure connect.
How Video.js Handles HLS and DASH Adaptive Streaming
This is where Video.js earns its place in a streaming stack. A plain MP4 is a single file at a single quality. Adaptive bitrate streaming splits video into short segments at multiple qualities so the player can switch on the fly based on the viewer’s connection.
Safari plays HLS natively, but Chrome, Firefox, and Edge do not. Video.js closes that gap with a bundled engine called VHS (videojs-http-streaming), included by default since version 7. VHS uses Media Source Extensions to play both HLS and MPEG-DASH in browsers that lack native support.
To play an HLS stream, point the source at your .m3u8 playlist and set the correct MIME type:
const player = videojs('my-player');
player.src({
src: 'https://cdn.example.com/stream/master.m3u8',
type: 'application/x-mpegURL'
});
For DASH, the type is application/dash+xml and the source points at your .mpd manifest. VHS handles the rest: parsing the .m3u8 playlist, downloading segments, measuring bandwidth, and switching renditions. You do not write any of that logic yourself.
One thing to keep in mind: Video.js plays the stream, but it does not create it. You still need something upstream to transcode your source video into HLS or DASH renditions and deliver them over a CDN. That is the backend half of the stack, and it is where a hosted API saves you the most time.
Video.js Plugins and Skins
Two things make Video.js flexible enough for production: its plugin system and its themeable skins.
The Plugin Ecosystem
Plugins extend the player without touching its core. Because everything is a component, a plugin can add UI, hook into events, or change playback behavior. Common categories include:
- Advertising: IMA and other plugins insert pre-roll, mid-roll, and post-roll ads
- Quality selection: Add a menu so viewers can pick a resolution manually
- Analytics: Track play, pause, watch time, and completion
- Thumbnails: Show preview images on hover over the progress bar
- Overlays: Display custom HTML at set times during playback
You can also write your own plugin in a few lines by registering it with videojs.registerPlugin(). This is how teams add product specific features like interactive hotspots or custom share buttons.
Skins and Customization
Video.js is a customizable video player at the CSS level. The default skin is a clean dark theme, but every element uses documented CSS classes you can override. You can change colors, fonts, control layout, and button icons without modifying JavaScript.
For responsive layouts, the fluid: true option makes the player scale to its container while keeping the aspect ratio. There is also a responsive option that adjusts the control bar for small screens. Community skins and theme packages give you a starting point if you do not want to design from scratch.
Using Video.js with React, Vue, and Other Frameworks
Video.js works with any framework, but the integration pattern matters because the library manipulates the DOM directly and frameworks like React expect to own the DOM.
Video.js React Integration
The recommended Video.js React approach is to create the player inside a useEffect hook, store the instance in a ref, and dispose of it on unmount. This keeps Video.js and React from fighting over the same element.
import React, { useEffect, useRef } from 'react';
import videojs from 'video.js';
import 'video.js/dist/video-js.css';
export const VideoPlayer = ({ options }) => {
const videoRef = useRef(null);
const playerRef = useRef(null);
useEffect(() => {
if (!playerRef.current) {
const videoElement = document.createElement('video-js');
videoRef.current.appendChild(videoElement);
playerRef.current = videojs(videoElement, options);
}
return () => {
if (playerRef.current && !playerRef.current.isDisposed()) {
playerRef.current.dispose();
playerRef.current = null;
}
};
}, [options]);
return <div ref={videoRef} />;
};
The version 10 beta ships an official @videojs/react package that handles this wiring for you. For a wider look at building playback in React, including other libraries, see our roundup of the best React video player options. The same disposal pattern applies in Vue, Angular, and Svelte: create the player when the component mounts, tear it down when it unmounts. For mobile, our React Native video example covers the equivalent approach.
Advantages and Limitations of Video.js
Video.js is a strong default, but it is not the right tool for every job. Here is an honest read.
Advantages
- Consistent cross-browser playback without writing browser specific code
- Free and open source under the MIT license, with no per-view fees
- HLS and DASH support built in through VHS
- Deep customization via CSS skins and a component tree
- Large plugin ecosystem for ads, analytics, and quality selection
- Framework friendly with established patterns for React, Vue, and Angular
- Active maintenance and a big community after 15+ years
Limitations
- It is only the player. It does not encode, host, or deliver video. You still need backend infrastructure.
- No built in DRM. Premium content protection requires additional plugins and a license service.
- Bundle size. The full library plus VHS adds weight, which matters for performance budgets.
- Framework friction. Integrating with React or Vue requires care around the DOM and cleanup.
- You own the operations. Transcoding, storage, and CDN delivery are your responsibility.
That last point is the important one. Video.js solves the frontend cleanly, which throws the backend into sharp relief. A .m3u8 URL has to come from somewhere.
Where Video.js Fits in a Production Video Stack
Video.js is the frontend layer. On its own, it needs three things behind it: video that has been transcoded into adaptive renditions, storage for those files, and a CDN to deliver them fast worldwide. Building that pipeline yourself means running encoders, managing storage, and stitching together CDN accounts before you can hand a single URL to your player.
This is the point where a video infrastructure API does the heavy lifting. LiveAPI handles the entire backend so Video.js only has to play the result. You upload or stream a source, LiveAPI encodes it instantly into adaptive bitrate HLS, and you get back an HLS URL you drop straight into Video.js.
Feeding Video.js from LiveAPI
The flow is short. LiveAPI’s video encoding turns your source into HLS with multiple quality renditions, delivered across Akamai, Cloudflare, and Fastly. You take the returned .m3u8 URL and set it as the Video.js source:
player.src({
src: 'https://your-stream.liveapi.com/master.m3u8',
type: 'application/x-mpegURL'
});
For live content, LiveAPI’s live streaming API ingests via RTMP or SRT from any encoder and outputs HLS, so the same Video.js setup that plays on-demand files also plays your live streams. Because LiveAPI records live streams to VOD automatically, a stream can turn into an on-demand asset without extra work.
If you are building the full application rather than a single embed, LiveAPI works as a complete video hosting API and on-demand Video API, with instant transcoding and pay-as-you-grow pricing. Our guide on how to build a video streaming app shows how the pieces fit together end to end.
Is Video.js Right for Your Project?
Video.js is a good fit if most of these describe you:
- You want a ready made, skinnable player UI rather than building one from scratch
- You need consistent playback and controls across all browsers and devices
- You want HLS or DASH support without writing streaming logic
- You value an open source, no-license-fee option
- You expect to extend the player with ads, analytics, or custom features
It is less ideal if you only need a raw streaming engine with your own UI (use hls.js or Shaka directly), if you need turnkey DRM out of the box, or if bundle size is your tightest constraint. In those cases a lighter or more specialized player may serve you better.
Whichever player you choose, remember it is only half the system. Pairing a frontend player like Video.js with a backend that handles encoding, hosting, and delivery is what turns a demo into a product.
Video.js FAQ
Is Video.js free to use?
Yes. Video.js is open source and released under the MIT license, so it is free for commercial and personal projects. There are no per-view or per-player fees. You only pay for the backend infrastructure that encodes, stores, and delivers your video.
Does Video.js support HLS streaming?
Yes. Video.js has HLS support built in through its VHS engine (videojs-http-streaming), bundled by default since version 7. It plays HLS in Chrome, Firefox, and Edge using Media Source Extensions, and uses Safari’s native HLS where available. It also supports MPEG-DASH.
What is the difference between Video.js and hls.js?
Video.js is a full player with a UI, controls, plugins, and skins that also handles adaptive streaming. hls.js is a streaming engine that only adds HLS playback to the browser and leaves the interface to you. Video.js uses a similar engine internally, so many teams use Video.js when they want the complete player.
How do I use Video.js with React?
Create the player inside a useEffect hook, hold the instance in a ref, and dispose of it when the component unmounts. This prevents React and Video.js from conflicting over the DOM. The version 10 beta also offers an official @videojs/react package that manages this for you.
Can Video.js play live streams?
Yes. Video.js plays live HLS and DASH streams the same way it plays on-demand ones, by pointing the source at a live .m3u8 or .mpd manifest. You need a backend that ingests your live feed and outputs an adaptive live stream, which is what a live streaming API provides.
What is the latest version of Video.js?
The stable line is version 8.x, with builds like 8.23.9 available on the CDN. The project also maintains a version 10 beta that was rebuilt for modern frameworks and ships an official React package. Most production sites run the 8.x branch today.
Does Video.js work on mobile?
Yes. Video.js works on iOS and Android browsers, tablets, and web based smart TVs. The fluid and responsive options adjust the layout and control bar for small screens so the player scales cleanly across devices.
Do I need a CDN with Video.js?
For a few small files you can serve video directly, but for any real audience you need a CDN so segments load fast worldwide and playback does not buffer. Video.js does not include delivery, so you pair it with a CDN backed video API that outputs HLS and serves it globally.
Getting Started with Video.js
Video.js gives you a consistent, customizable, open source player that handles the messy parts of browser video: cross-browser controls, HLS and DASH playback, captions, and a plugin system you can build on. Add it from a CDN in five minutes or install it with npm for a real app, then point it at an HLS source and you have production grade playback.
The player is only half the picture. To feed Video.js you need video that has been encoded into adaptive renditions and delivered over a fast CDN, and that is exactly what LiveAPI provides. Get started with LiveAPI to turn any source into an HLS stream your Video.js player can play in seconds, with instant encoding, multiple CDNs, and RTMP or SRT ingest for live.


