▶ composable-video-player

Getting started

composable-video-player is an HTML5 video player written in TypeScript. It has a typed API, a plugin system, and a UI that you describe as a JSON layout tree. It understands the PlayerJS source syntax and has a PlayerJS-compatible facade, so new Playerjs({...}) pages can switch to it. There are no runtime dependencies. hls.js is downloaded on demand, and only when an HLS (.m3u8) source needs it.

Features

Install

npm install composable-video-player

The package ships an ES module (dist/player.js), a UMD bundle (dist/player.umd.cjs / dist/player.umd.js), and TypeScript declarations. The CSS is bundled: styles are injected into <head> the first time a player is created, so there is no stylesheet to import. See CDN and builds.

Quick start (bundler)

<div id="player"></div>
import { createPlayer } from 'composable-video-player';

const player = createPlayer({
  container: 'player', // element id, or an HTMLElement
  source: '[360p]https://example.com/v360.mp4,[720p]https://example.com/v720.mp4',
  title: 'My video',
  poster: 'https://example.com/poster.jpg',
  subtitle: '[English]https://example.com/en.vtt',
  thumbnails: 'https://example.com/thumbs.vtt',
});

player.on('time', (t) => console.log('time', t));
player.on('end', () => console.log('finished'));

player.command('setQuality', 0); // switch to 360p
player.seek(30);
await player.play();

container takes an element id (no #, it is looked up with document.getElementById) or an HTMLElement. Creating a second player in the same container destroys the first one.

createPlayer includes all default plugins. If you want a smaller bundle, use createPlayerCore and pick the plugins you need:

import { createPlayerCore, qualityPlugin, subtitlesPlugin, hotkeysPlugin } from 'composable-video-player';

const player = createPlayerCore({
  container: 'player',
  source: 'https://example.com/video.mp4',
  plugins: [qualityPlugin(), subtitlesPlugin(), hotkeysPlugin()],
});

Script tag (UMD from a CDN)

The UMD bundle exposes the whole public API as window.VP and the PlayerJS-compatible constructor as window.Playerjs.

<div id="player"></div>
<script src="https://cdn.jsdelivr.net/npm/composable-video-player@0.1/dist/player.umd.js"></script>
<script>
  var player = VP.createPlayer({
    container: 'player',
    source: 'https://example.com/video.mp4',
    title: 'My video',
  });
  player.on('play', function () { console.log('playing'); });
</script>

ES module from a CDN

<div id="player"></div>
<script type="module">
  import { createPlayer } from 'https://cdn.jsdelivr.net/npm/composable-video-player@0.1/dist/player.js';

  createPlayer({ container: 'player', source: 'https://example.com/video.mp4' });
</script>

CDN and builds covers version pinning, unpkg, and self-hosting.

Sizing and aspect ratio

The player fills the container's width (.vp-root { width: 100% }) and gets its height from an aspect ratio:

ratio value Result
(omitted) 16:9
'4:3', '21:9', '16/9', '2.35:1' That ratio (W:H or W/H, decimals allowed)
'auto' 16:9 until metadata loads, then the video's own aspect ratio

To limit the size, size the container:

#player { max-width: 960px; margin: 0 auto; }

For a container with a fixed height (a full-viewport hero, a grid cell, and so on), give the player root a height as well. aspect-ratio has no effect once both dimensions are set:

#player { height: 100vh; }
#player .vp-root { height: 100%; }

The video uses object-fit: contain (letterboxing on a black background). In fullscreen the root fills the screen and ignores the ratio.

TypeScript

Browser support

The player targets current evergreen browsers. The JavaScript is compiled to ES2020. The stylesheet uses color-mix(), overflow: clip, container queries, aspect-ratio, and the individual translate/scale properties. In practice that means Chrome/Edge 111+, Firefox 113+, and Safari 16.2+. Internet Explorer and legacy Edge are not supported.

Platform notes: