--- 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 UI Components can go anywhere inside a ``. However, you should consider placing your components in ``. Components in `` will go fullscreen with the player, respond to user activity, and more. Read more about `` UI Components can go anywhere inside a ``. However, you should consider placing your components in ``. Components in `` will go fullscreen with the player, respond to user activity, and more. Read more about `` ## Styling and customization ### 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 ( )} /> ``` `className` and `style` also accept functions of state for dynamic styling without a full render prop: ```tsx 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. 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 Play Pause ``` ```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. ## Compound components Complex interactions are split into composable parts. A parent manages shared state while children consume it. Each part is still one element. Compound components use dot notation off a shared namespace: ```tsx ``` ```html ```