UI Components
Understanding Video.js v10's primitive-based component architecture
Video.js v10 components are built from unstyled UI primitives , taking inspiration from projects like shadcn/ui , Base UI , and Media Chrome . This approach gives you control over styling and behavior while handling the complex interactions for you.
All UI primitives use the media-* namespace : media-play-button, media-time-slider, media-volume-slider. This naming convention is shared with Media Chrome for familiarity and interoperability.
UI primitives are React components with standard PascalCase naming: PlayButton, TimeSlider, VolumeSlider.
Core Principles
1. Primitive-Based Design
Each primitive represents at most one HTML element , without internal elements.
<TimeSlider.Root>
<TimeSlider.Track> {/* One element: the track */}
<TimeSlider.Progress /> {/* One element: the progress bar */}
<TimeSlider.Pointer /> {/* One element: hover indicator */}
</TimeSlider.Track>
<TimeSlider.Thumb /> {/* One element: the draggable thumb */}
</TimeSlider.Root><media-time-slider>
<div class="track"> <!-- One element: the track -->
<div class="progress"></div> <!-- One element: the progress bar -->
<div class="pointer"></div> <!-- One element: hover indicator -->
</div>
<div class="thumb"></div> <!-- One element: the draggable thumb -->
</media-time-slider>2. Compound Components
Complex components are broken into smaller, composable pieces. This pattern is used for:
- Sliders - Root, Track, Thumb, Progress, Pointer
- Tooltips - Root, Trigger, Positioner, Popup
- Popovers - Root, Trigger, Positioner, Popup
Each piece handles one concern, but they work together seamlessly.
3. Built-in Accessibility
Components include proper ARIA attributes, keyboard navigation, and focus management out of the box:
- Buttons have correct roles and labels
- Sliders support arrow key navigation
- Focus is managed properly in tooltips and popovers
- Screen readers get meaningful announcements
4. Data Attributes for Styling
Components expose state through data attributes, allowing pure CSS state styling:
/* Style based on state without JavaScript */
button[data-paused] {
/* Paused state */
}
button:not([data-paused]) {
/* Playing state */
}Common data attributes:
-
data-paused- Present when media is paused -
data-muted- Present when audio is muted -
data-fullscreen- Present when in fullscreen mode -
data-volume-level- Values:high,medium,low,off
Available Components
Simple Components
Single-element components that handle one piece of UI:
- PlayButton - Play/pause toggle
- MuteButton - Audio mute toggle
- FullscreenButton - Fullscreen toggle
- CurrentTimeDisplay - Current playback time
- DurationDisplay - Total media duration
- PreviewTimeDisplay - Time at hover position
Compound Components
Multi-element components for complex interactions:
- TimeSlider - Seekable timeline with progress
- VolumeSlider - Volume control slider
- Tooltip - Hover tooltips with positioning
- Popover - Click/hover popovers with collision detection
Context Binding
UI components automatically bind to the nearest player and media in the DOM tree. You don’t need to pass props or wire up event handlers—components discover their context.
<website-video-player>
<website-frosted-video-skin>
<video src="video.mp4"></video>
</website-frosted-video-skin>
<!-- This button automatically binds to the player above -->
<media-play-button></media-play-button>
</website-video-player><Provider>
<FrostedSkin>
<Video src="video.mp4" />
</FrostedSkin>
{/* This button automatically binds to the Provider above */}
<PlayButton />
</Provider>This pattern means you can:
- Place controls anywhere inside the player tree
- Create custom layouts without prop drilling
- Mix skins and individual components as needed
See Also
- Skins — Pre-composed component collections
- Architecture — How components fit in the three-pillar model
- Presets — Feature collections that power components