# Why Video.js?

The open-source video player for React and HTML. Lightweight, accessible components built for performance and streaming.

Video.js is the open-source video player for the web. It makes web video easy, performant, accessible, and customizable.

Let’s dig into when you’d want to use Video.js, starting with the question we hear most…

## Why not `<video>`?

The native `<video>` tag is enough for a short clip that plays without controls. Once viewers control playback, the gaps show up quickly.

### Cross-browser inconsistency

Chrome, Safari, and Firefox each render the default `<video>` controls differently. You can hide them, but you can’t deeply style or rearrange them. Building UI that matches your product means starting from scratch.

![Chrome's default video controls](https://videojs.org/_astro/chrome-default-controls.D2ybhMTL.png)

Chrome

![Safari's default video controls](https://videojs.org/_astro/safari-default-controls.CPUt6ekS.png)

Safari

![Firefox's default video controls](https://videojs.org/_astro/firefox-default-controls.CYiJeU98.png)

Firefox

### Accessibility

The browser’s controls bring their own accessibility behavior, and it differs by browser. Once you replace them, accessibility is your job: ARIA roles and labels that follow player state, keyboard controls and shortcuts, focus management, screen reader announcements when playback changes, controls that work with touch, tooltips and menus that stay on screen, and respect for reduced-motion and contrast preferences. Video.js components and skins handle these by default, following the WAI-ARIA Authoring Practices.

- [Learn more about accessibility](https://videojs.org/docs/framework/html/guides/accessibility)

### Player state

Custom controls need to know what the media is doing. Even something as simple as “playing” or “buffering” comes from several events, behaves differently across browsers, streaming engines, and services like YouTube, and has to reset when the source changes. Wiring that by hand is fragile, and in React it quickly leads to tangled effects and extra renders.

Video.js keeps playback state in one player store that every component reads. Components reflect state as data attributes such as `data-paused`, so your CSS follows the player without extra code, and controls a browser can’t support, such as picture-in-picture, hide themselves. In React, `usePlayer` with a selector re-renders a component only when the state it selects changes.

- [Learn more about the architecture](https://videojs.org/docs/framework/html/guides/architecture)

### Video interactions

Video often involves more than an MP4 in `src`. Features like thumbnail previews, adaptive bitrate, quality selection, chapters, audio track switching, localized controls, error UI, live-streaming UI, AirPlay, and DRM are *possible* with the video element, but require complex wiring and careful work on maintainability and [accessibility](https://videojs.org/docs/framework/html/guides/accessibility).

Video.js provides accessible, composable building blocks for these common playback and streaming features. Other integrations, including ads and emerging media formats, remain under development as version 10 matures.

### Streaming formats and sources

Behind features like adaptive bitrate and quality selection are streaming formats such as HLS and DASH. They split video into segments that a playback engine can select based on bandwidth and preferences. Choose the matching Video.js media component to play these formats across browsers while keeping the surrounding player state and UI consistent.

Of course, your video might not be coming from a `src` like that. It might be provided by a service. However, services like YouTube, Vimeo, and Mux each speak their own API. Video.js gives their media components a consistent interface, so changing providers doesn’t require rebuilding the player around them.

That boundary also gives us a path to future sources such as canvas-based MoQ playback and use cases such as animated GIF replacement without rebuilding the controls around them.

## Why Video.js?

Video.js is built at Mux by the teams behind Video.js, Plyr, Vidstack, and Media Chrome, with contributions from engineers across other player projects. Between us, our projects have served tens of billions of monthly video plays. Most of the choices on this page came from things we got wrong the first time, then the second, then the fifth. We’ve been supporting your edge cases for over a decade. Now we’re shipping the player we always wanted to build.

Let’s dig into some of the design principles that help explain why we built Video.js and why it may be the best choice for your project or team.

### Add only what you need

Most player libraries ship every feature in one bundle, so you carry code you don’t run. If your video doesn’t need a feature like DRM, why ship that code? Video.js turns features into independent, *composable* modules. You hand `createPlayer` the array you actually want, and what you don’t import doesn’t ship.

```ts
import { createPlayer, playbackFeature, timeFeature } from '@videojs/html';

const { PlayerElement, PlayerController } = createPlayer({
  features: [playbackFeature, timeFeature],
});
```

- [Learn more about features](https://videojs.org/docs/framework/html/guides/features)

Of course, you don’t have to think about every feature you’re going to need if you don’t want to. Pre-built feature bundles are still there if you’d rather not assemble the list by hand.

- [Learn more about presets](https://videojs.org/docs/framework/html/guides/presets)

### Framework-native

Many player libraries wrap a single web component for every framework. That works, until you reach the seams: refs you can’t pass through, state that doesn’t reconcile, prop names that aren’t quite React or HTML. Video.js ships idiomatic APIs for its supported frameworks: React components and hooks for React, and custom elements and controllers for HTML.

The player feels like the rest of your app.

**React**

```tsx
import { VideoPlayer, Video, VideoSkin } from '@videojs/react/video';
import '@videojs/react/video/skin.css';

function MyPlayer() {
  return (
    <VideoPlayer>
      <VideoSkin>
        <Video src="movie.mp4" />
      </VideoSkin>
    </VideoPlayer>
  );
}
```

**HTML**

```html
<script type="module">
  import '@videojs/html/video/player';
  import '@videojs/html/video/skin';
</script>

<video-player>
  <video-skin>
    <video src="movie.mp4"></video>
  </video-skin>
</video-player>
```

### Source you can change

Most player libraries draw a line between “configurable through props” and “fork the source.” Cross the line and you’re on your own. Video.js lets you add the files for any skin to your project: components in your framework’s language that you can read and change. The rest of the player keeps working underneath it.

We don’t want to cram every possible player into one configuration API. Packaged skins cover common visual changes. For everything else, add the skin source to your project and compose the UI you need.

- [Customize skins](https://videojs.org/docs/framework/html/guides/customize-skins#style-skin-source)

### AI-native DX

Documentation aimed at human readers usually doesn’t help an agent. Video.js publishes an `llms.txt` index, ships every docs page as both HTML and Markdown, and has a dedicated guide for building with AI assistants. Drop the player into a Claude or Cursor session and the docs come along for the ride.

- [Build with AI](https://videojs.org/docs/framework/html/guides/build-with-ai)

## Related pages

### Guides

- [Installation](https://videojs.org/docs/guides/installation/html): Install Video.js packages and build an accessible, customizable video player with composable controls
- [Architecture](https://videojs.org/docs/framework/html/guides/architecture): How Video.js players are structured — state, UI, media, and extensions

---

HTML documentation: https://videojs.org/docs/framework/html/llms.txt
All documentation: https://videojs.org/llms.txt
