Skip to main content
@videojs/html provides a set of custom elements that work in any browser without a JS framework. Each element registers itself via a side-effect import.

Prerequisites

@videojs/html installed (see Installation).

Quick start

1

Import the custom elements

Each element registers itself as a global custom element via a side-effect import. Import the player, skin, and CSS:
index.ts
import '@videojs/html/video/player';
import '@videojs/html/video/skin';
import '@videojs/html/video/skin.css';
2

Add the markup

Use <video-player> as the root, <video-skin> as the UI wrapper, and a standard <video> with slot="media" as the media source:
index.html
<video-player>
  <video-skin>
    <video slot="media" src="https://example.com/movie.mp4"></video>
  </video-skin>
</video-player>

Available presets

Each preset provides a player element, one or more skin elements, and an optional media element:
PresetPlayer elementSkin elementsMedia element
/video<video-player><video-skin>, <video-minimal-skin><video>
/audio<audio-player><audio-skin>, <audio-minimal-skin><audio>
/background<background-video-player><background-video-skin><background-video>

Audio preset example

import '@videojs/html/audio/player';
import '@videojs/html/audio/skin';
import '@videojs/html/audio/skin.css';
<audio-player>
  <audio-skin>
    <audio slot="media" src="https://example.com/podcast.mp3"></audio>
  </audio-skin>
</audio-player>

Background video preset example

import '@videojs/html/background/player';
import '@videojs/html/background/video';
import '@videojs/html/background/skin';
import '@videojs/html/background/skin.css';
<background-video-player>
  <background-video-skin>
    <background-video slot="media" src="hero.mp4"></background-video>
  </background-video-skin>
</background-video-player>

Individual UI elements

You can import individual UI elements from @videojs/html/ui/* to compose your own layout without using a preset skin:
import '@videojs/html/video/player';
import '@videojs/html/media/container';
import '@videojs/html/ui/play-button';
import '@videojs/html/ui/mute-button';
import '@videojs/html/ui/volume-slider';
import '@videojs/html/ui/fullscreen-button';
import '@videojs/html/ui/time-slider';
<video-player>
  <media-container>
    <video slot="media" src="movie.mp4"></video>
    <media-play-button>Play / Pause</media-play-button>
    <media-time-slider></media-time-slider>
    <media-mute-button>Mute</media-mute-button>
    <media-volume-slider></media-volume-slider>
    <media-fullscreen-button>Fullscreen</media-fullscreen-button>
  </media-container>
</video-player>
Available UI elements include:
ElementPurpose
<media-play-button>Play / pause toggle
<media-mute-button>Mute / unmute toggle
<media-volume-slider>Volume level slider
<media-time-slider>Seek / progress slider
<media-fullscreen-button>Fullscreen toggle
<media-pip-button>Picture-in-picture toggle
<media-seek-button>Jump forward or backward by seconds
<media-playback-rate-button>Cycle through playback rates
<media-captions-button>Toggle captions
<media-buffering-indicator>Shown while buffering
<media-container>Layout wrapper for media + controls
<media-controls>Controls bar

State reflection via data-* attributes

Custom elements reflect player state as data-* attributes on the element. Use CSS to respond to state changes:
/* Show play icon only when paused */
media-play-button .play-icon { display: none; }
media-play-button[data-paused] .play-icon { display: inline; }

/* Show pause icon only when playing */
media-play-button .pause-icon { display: none; }
media-play-button:not([data-paused]) .pause-icon { display: inline; }

/* Hide fullscreen button on platforms that don't support it */
media-fullscreen-button[data-availability="unsupported"] { display: none; }

Access state in JavaScript with PlayerController

Use PlayerController to read player state and call actions from JavaScript. Import a feature selector such as selectPlayback to subscribe to a specific slice of state:
import { createPlayer, playback, volume, time, MediaElement } from '@videojs/html';
import { selectPlayback } from '@videojs/html';

const { ProviderMixin, context } = createPlayer({
  features: [playback, volume, time],
});

class PlayToggle extends HTMLElement {
  readonly #playback = new PlayerController(this, context, selectPlayback);

  connectedCallback() {
    this.addEventListener('click', () => {
      const state = this.#playback.value;
      if (state?.paused) {
        state.play();
      } else {
        state?.pause();
      }
    });
  }
}

Create a custom player element

Use createPlayer() to define a player element with a specific set of features, then extend ProviderMixin to register it as a custom element:
import { createPlayer, playback, volume, time, fullscreen, MediaElement } from '@videojs/html';

const { ProviderMixin } = createPlayer({
  features: [playback, volume, time, fullscreen],
});

class MyVideoPlayer extends ProviderMixin(MediaElement) {
  static readonly tagName = 'my-video-player';
}

customElements.define('my-video-player', MyVideoPlayer);
<my-video-player>
  <media-container>
    <video slot="media" src="movie.mp4"></video>
    <media-play-button>Play</media-play-button>
  </media-container>
</my-video-player>

Next steps

Media sources

Add HLS, DASH, and other streaming formats.

Custom features

Compose and extend the player’s feature set.

Custom skins

Build your own skin from individual UI elements.

Build docs developers (and LLMs) love