---
title: UI components
description: How Video.js UI components work — one element per component, data attributes for state, and compound composition.
---

import FrameworkCase from '@/components/docs/FrameworkCase.astro';
import DocsLink from '@/components/docs/DocsLink.astro';
import DocsLinkCard from '@/components/docs/DocsLinkCard.astro';

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

Every UI component renders exactly one HTML element, taking inspiration from projects like [shadcn/ui](https://ui.shadcn.com/) and [Base UI](https://base-ui.com/). This approach gives you control over styling and behavior while handling the complex interactions for you.

## Where to put your components
<FrameworkCase frameworks={["react"]}>
UI Components can go anywhere inside a <DocsLink slug="reference/player-provider">`<Player.Provider>`</DocsLink>.

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

<DocsLinkCard slug="reference/player-container">Read more about `<Player.Container>`</DocsLinkCard>
</FrameworkCase>

<FrameworkCase frameworks={["html"]}>
UI Components can go anywhere inside a <DocsLink slug="reference/player-provider">`<video-player>`</DocsLink>.

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

<DocsLinkCard slug="reference/player-container">Read more about `<player-container>`</DocsLinkCard>
</FrameworkCase>

## Styling and customization

<FrameworkCase frameworks={["react"]}>

### The `render` prop

The `render` prop is the primary customization mechanism. It accepts a function that receives `props` and `state`, and returns your element. The `props` object includes event handlers, ARIA attributes, data attributes, and a ref — always spread `{...props}` to keep everything working:

```tsx
<PlayButton
  render={(props, state) => (
    <button {...props}>
      {state.paused ? 'Play' : 'Pause'}
    </button>
  )}
/>
```

`className` and `style` also accept functions of state for dynamic styling without a full render prop:

```tsx
<PlayButton
  className={(state) =>
    state.paused ? 'btn btn--paused' : 'btn btn--playing'
  }
/>
```

### Data attributes and CSS custom properties

Components reflect player state as `data-*` attributes on their element. For example, `data-paused` or `data-volume-level="high"`.

This lets you style state changes in pure CSS:

```css
/* Show/hide icons based on play state */
.play-icon  { display: none; }
.pause-icon { display: none; }

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

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.

</FrameworkCase>

<FrameworkCase frameworks={["html"]}>

Components reflect player state as `data-*` attributes on their element. For example, `data-paused` or `data-volume-level="high"`.

This lets you style state changes in pure CSS:

```html
<media-play-button class="my-play-button">
  <span class="play-icon">Play</span>
  <span class="pause-icon">Pause</span>
</media-play-button>
```

```css
media-play-button .play-icon  { display: none; }
media-play-button .pause-icon { display: none; }

media-play-button[data-paused] .play-icon        { display: inline; }
media-play-button:not([data-paused]) .pause-icon  { display: inline; }
```

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.

</FrameworkCase>

## Compound components

Complex interactions are split into composable parts. A parent manages shared state while children consume it. Each part is still one element.

<FrameworkCase frameworks={["react"]}>

Compound components use dot notation off a shared namespace:

```tsx
<VolumeSlider.Root orientation="vertical">
  <VolumeSlider.Track>
    <VolumeSlider.Fill />
  </VolumeSlider.Track>
  <VolumeSlider.Thumb />
</VolumeSlider.Root>
```

</FrameworkCase>

<FrameworkCase frameworks={["html"]}>

```html
<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>
```

</FrameworkCase>
