Skip to content
FrameworkStyle

UI components

How Video.js UI components use focused elements, data attributes for state, and compound composition.

UI components are controls like buttons, sliders, and time displays.

Visible UI components render at most one HTML element, taking inspiration from projects like shadcn/ui and Base UI. This approach gives you control over styling and behavior while handling the complex interactions for you. State-only components render no markup, and controls can skip rendering when their feature is unavailable.

Individual controls provide interaction, accessible names, and state. You add their visible text or icons and CSS. Sliders also need the child elements and styles shown on their reference pages.

Where to put your components

UI components can go anywhere inside a <video-player>.

However, you should consider placing your components in <media-container>. Components in <media-container> will go fullscreen with the player, respond to user activity, and more.

Styling and customization

For components you add yourself, select them by their custom element name. Use the documented state data-* attributes and CSS custom properties to style them.

Add icons

Import @videojs/html/icons/element once in your app.ts to register <media-icon>. Choose an icon with name. Set family="minimal" to use the Minimal skin’s icon designs. You can also use your own text or SVG.

import '@videojs/html/icons/element';
<media-play-button class="my-play-button">
  <media-icon class="play-icon" name="play"></media-icon>
  <media-icon class="pause-icon" name="pause"></media-icon>
</media-play-button>

Style components with state

Components add data-* attributes to describe their current state. For example, a play button adds data-paused while the media is paused. Use those attributes in CSS:

.my-play-button .pause-icon {
  display: none;
}

.my-play-button:defined:not([data-paused]) .play-icon {
  display: none;
}

.my-play-button:defined:not([data-paused]) .pause-icon {
  display: inline-flex;
}

The browser matches :defined after it loads the custom element. This keeps the play icon visible while JavaScript loads.

Each component’s reference page documents its full set of data attributes.

Some components also expose CSS custom properties for continuous values like fill percentage and pointer position. Sliders, for example, set --media-slider-fill and --media-slider-pointer. See individual component reference pages for specifics.

Compound components

Complex interactions are split into composable parts. A parent manages shared state while children consume it. Each visible part owns at most one element.

<media-volume-slider orientation="vertical">
  <media-slider-track>
    <media-slider-fill></media-slider-fill>
  </media-slider-track>
  <media-slider-thumb></media-slider-thumb>
</media-volume-slider>