Slider
A composable slider component with track, fill, thumb, preview, and value parts
Import
import { Slider } from '@videojs/react';import '@videojs/html/ui/slider';Anatomy
<Slider.Root>
<Slider.Track>
<Slider.Fill />
</Slider.Track>
<Slider.Thumb />
<Slider.Preview>
<Slider.Value type="pointer" />
</Slider.Preview>
</Slider.Root><media-slider>
<media-slider-track>
<media-slider-fill></media-slider-fill>
</media-slider-track>
<media-slider-thumb></media-slider-thumb>
<media-slider-preview>
<media-slider-value type="pointer"></media-slider-value>
</media-slider-preview>
</media-slider>Behavior
The base Slider provides a generic range input. It manages value, pointer tracking, and drag interactions. Domain-specific sliders like TimeSlider and VolumeSlider extend this with media-specific bindings. TimeSlider also provides parts for rendering chapter ranges and the chapter at the current interaction position.
The slider supports vertical orientation via the orientation prop (defaults to "horizontal").
Styling
Use CSS custom properties to position fill, thumb, and preview elements:
media-slider-fill {
width: var(--media-slider-fill);
}
media-slider-thumb {
left: var(--media-slider-fill);
}React renders standard DOM elements. Add a className to style them:
.slider-fill {
width: var(--media-slider-fill);
}
.slider-thumb {
left: var(--media-slider-fill);
}Style based on interaction state:
media-slider[data-interactive] media-slider-track {
height: 6px;
}
media-slider[data-pointing] media-slider-preview {
opacity: 1;
}.slider[data-interactive] .slider-track {
height: 6px;
}
.slider[data-pointing] .slider-preview {
opacity: 1;
}Accessibility
Renders with role="slider" and automatic ARIA attributes (aria-valuemin, aria-valuemax, aria-valuenow, aria-valuetext). Override the label with the label prop. Keyboard controls:
- Arrow Left / Arrow Right: step by
stepincrement - Page Up / Page Down: step by
largeStepincrement - Home: jump to minimum
- End: jump to maximum
Examples
Basic
A slider with track, fill, and thumb.
import { Slider } from '@videojs/react';
import { useState } from 'react';
export default function BasicUsage() {
const [value, setValue] = useState(50);
return (
<div className="demo">
<Slider.Root className="media-slider" value={value} onValueChange={setValue}>
<Slider.Track className="media-slider-track">
<Slider.Fill className="media-slider-fill" />
</Slider.Track>
<Slider.Thumb className="media-slider-thumb" />
</Slider.Root>
</div>
);
}
.demo {
display: flex;
align-items: center;
padding: 24px;
background: #1a1a1a;
}
.media-slider {
position: relative;
display: flex;
align-items: center;
width: 100%;
height: 20px;
cursor: pointer;
}
.media-slider-track {
position: absolute;
right: 0;
left: 0;
height: 4px;
background: rgba(255, 255, 255, 0.3);
border-radius: 9999px;
transition: height 150ms ease;
}
.media-slider[data-interactive] .media-slider-track {
height: 6px;
}
.media-slider-fill {
position: absolute;
top: 0;
left: 0;
width: var(--media-slider-fill);
height: 100%;
background: white;
border-radius: 9999px;
}
.media-slider-thumb {
position: absolute;
left: var(--media-slider-fill);
width: 14px;
height: 14px;
background: white;
border-radius: 50%;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
transform: translateX(-50%) scale(0);
transition: transform 150ms ease;
}
.media-slider[data-interactive] .media-slider-thumb {
transform: translateX(-50%) scale(1);
}
.media-slider[data-dragging] .media-slider-thumb {
transform: translateX(-50%) scale(1.1);
}
<div class="demo">
<media-slider class="media-slider" value="50">
<media-slider-track class="media-slider-track">
<media-slider-fill class="media-slider-fill"></media-slider-fill>
</media-slider-track>
<media-slider-thumb class="media-slider-thumb"></media-slider-thumb>
</media-slider>
</div>
.demo {
display: flex;
align-items: center;
padding: 24px;
background: #1a1a1a;
}
.media-slider {
position: relative;
display: flex;
align-items: center;
width: 100%;
height: 20px;
cursor: pointer;
}
.media-slider-track {
position: absolute;
right: 0;
left: 0;
height: 4px;
background: rgba(255, 255, 255, 0.3);
border-radius: 9999px;
transition: height 150ms ease;
}
.media-slider[data-interactive] .media-slider-track {
height: 6px;
}
.media-slider-fill {
position: absolute;
top: 0;
left: 0;
width: var(--media-slider-fill);
height: 100%;
background: white;
border-radius: 9999px;
}
.media-slider-thumb {
position: absolute;
left: var(--media-slider-fill);
width: 14px;
height: 14px;
background: white;
border-radius: 50%;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
transform: translateX(-50%) scale(0);
transition: transform 150ms ease;
}
.media-slider[data-interactive] .media-slider-thumb {
transform: translateX(-50%) scale(1);
}
.media-slider[data-dragging] .media-slider-thumb {
transform: translateX(-50%) scale(1.1);
}
import '@videojs/html/ui/slider';
With Preview
A slider with a pointer-tracking preview that displays the value at the current pointer position.
import { Slider } from '@videojs/react';
import { useState } from 'react';
export default function WithPreview() {
const [value, setValue] = useState(50);
return (
<div className="demo">
<Slider.Root className="media-slider" value={value} onValueChange={setValue}>
<Slider.Track className="media-slider-track">
<Slider.Fill className="media-slider-fill" />
</Slider.Track>
<Slider.Thumb className="media-slider-thumb" />
<Slider.Preview className="preview">
<Slider.Value type="pointer" className="media-slider-value" />
</Slider.Preview>
</Slider.Root>
</div>
);
}
.demo {
display: flex;
align-items: center;
padding: 40px 24px;
background: #1a1a1a;
}
.media-slider {
position: relative;
display: flex;
align-items: center;
width: 100%;
height: 20px;
cursor: pointer;
}
.media-slider-track {
position: absolute;
right: 0;
left: 0;
height: 4px;
background: rgba(255, 255, 255, 0.3);
border-radius: 9999px;
transition: height 150ms ease;
}
.media-slider[data-interactive] .media-slider-track {
height: 6px;
}
.media-slider-fill {
position: absolute;
top: 0;
left: 0;
width: var(--media-slider-fill);
height: 100%;
background: white;
border-radius: 9999px;
}
.media-slider-thumb {
position: absolute;
left: var(--media-slider-fill);
width: 14px;
height: 14px;
background: white;
border-radius: 50%;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
transform: translateX(-50%) scale(0);
transition: transform 150ms ease;
}
.media-slider[data-interactive] .media-slider-thumb {
transform: translateX(-50%) scale(1);
}
.media-slider[data-dragging] .media-slider-thumb {
transform: translateX(-50%) scale(1.1);
}
.preview {
position: absolute;
bottom: 100%;
margin-bottom: 6px;
pointer-events: none;
opacity: 0;
transition: opacity 150ms ease;
}
.media-slider[data-pointing] .preview {
opacity: 1;
}
.media-slider-value {
padding: 2px 6px;
font-size: 12px;
color: white;
white-space: nowrap;
background: rgba(0, 0, 0, 0.8);
border-radius: 4px;
}
<div class="demo">
<media-slider class="media-slider" value="50">
<media-slider-track class="media-slider-track">
<media-slider-fill class="media-slider-fill"></media-slider-fill>
</media-slider-track>
<media-slider-thumb class="media-slider-thumb"></media-slider-thumb>
<media-slider-preview class="preview">
<media-slider-value type="pointer" class="media-slider-value"></media-slider-value>
</media-slider-preview>
</media-slider>
</div>
.demo {
display: flex;
align-items: center;
padding: 40px 24px;
background: #1a1a1a;
}
.media-slider {
position: relative;
display: flex;
align-items: center;
width: 100%;
height: 20px;
cursor: pointer;
}
.media-slider-track {
position: absolute;
right: 0;
left: 0;
height: 4px;
background: rgba(255, 255, 255, 0.3);
border-radius: 9999px;
transition: height 150ms ease;
}
.media-slider[data-interactive] .media-slider-track {
height: 6px;
}
.media-slider-fill {
position: absolute;
top: 0;
left: 0;
width: var(--media-slider-fill);
height: 100%;
background: white;
border-radius: 9999px;
}
.media-slider-thumb {
position: absolute;
left: var(--media-slider-fill);
width: 14px;
height: 14px;
background: white;
border-radius: 50%;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
transform: translateX(-50%) scale(0);
transition: transform 150ms ease;
}
.media-slider[data-interactive] .media-slider-thumb {
transform: translateX(-50%) scale(1);
}
.media-slider[data-dragging] .media-slider-thumb {
transform: translateX(-50%) scale(1.1);
}
.preview {
position: absolute;
bottom: 100%;
margin-bottom: 6px;
pointer-events: none;
opacity: 0;
transition: opacity 150ms ease;
}
.media-slider[data-pointing] .preview {
opacity: 1;
}
.media-slider-value {
padding: 2px 6px;
font-size: 12px;
color: white;
white-space: nowrap;
background: rgba(0, 0, 0, 0.8);
border-radius: 4px;
}
import '@videojs/html/ui/slider';
API Reference
Rootmedia-slider
Props
| Prop | Type | Default | Details |
|---|---|---|---|
disabled | boolean | false | |
| |||
label | object | '' | |
| |||
largeStep | number | 10 | |
| |||
max | number | 100 | |
| |||
min | number | 0 | |
| |||
orientation | 'horizontal' | 'vertical' | 'horizontal' | |
| |||
step | number | 1 | |
| |||
thumbAlignment | 'center' | 'edge' | 'center' | |
| |||
value | number | 0 | |
| |||
State
render, className, and style props.| Property | Type | Details |
|---|---|---|
value | number | |
| ||
fillPercent | number | |
| ||
pointerPercent | number | |
| ||
dragging | boolean | |
| ||
pointing | boolean | |
| ||
interactive | boolean | |
| ||
orientation | 'horizontal' | 'vertical' | |
| ||
disabled | boolean | |
| ||
thumbAlignment | 'center' | 'edge' | |
| ||
Data attributes
| Attribute | Type | Details |
|---|---|---|
data-dragging | ||
| ||
data-pointing | ||
| ||
data-interactive | ||
| ||
data-orientation | 'horizontal' | 'vertical' | |
| ||
data-disabled | ||
| ||
CSS custom properties
| Variable | Details |
|---|---|
--media-slider-fill | |
| |
--media-slider-pointer | |
| |
--media-slider-buffer | |
| |
Events
| Event | Description |
|---|---|
drag-end | Fired when a pointer drag ends. |
drag-start | Fired when a pointer drag starts. |
value-change | Fired while the slider value changes during an interaction. |
value-commit | Fired when an interaction commits the slider value. |
Buffermedia-slider-buffer
Displays the buffered range on the slider track.
Props
| Prop | Type | Default | Details |
|---|---|---|---|
className | string | ((state: SliderState) => string | undefined) | — | |
| |||
render | ReactElement | ((props: HTMLProps, state: SliderState) => ReactElement | null) | — | |
| |||
style | CSSProperties | ((state: SliderState) => CSSProperties | undefined) | — | |
| |||
Fillmedia-slider-fill
Displays the filled portion from start to the current value.
Props
| Prop | Type | Default | Details |
|---|---|---|---|
className | string | ((state: SliderState) => string | undefined) | — | |
| |||
render | ReactElement | ((props: HTMLProps, state: SliderState) => ReactElement | null) | — | |
| |||
style | CSSProperties | ((state: SliderState) => CSSProperties | undefined) | — | |
| |||
Previewmedia-slider-preview
Positioning container for preview content that tracks the pointer along the slider.
Props
| Prop | Type | Default | Details |
|---|---|---|---|
className | string | ((state: SliderState) => string | undefined) | — | |
| |||
render | ReactElement | ((props: HTMLProps, state: SliderState) => ReactElement | null) | — | |
| |||
style | CSSProperties | ((state: SliderState) => CSSProperties | undefined) | — | |
| |||
Data attributes
| Attribute | Type | Details |
|---|---|---|
data-dragging | ||
| ||
data-pointing | ||
| ||
data-interactive | ||
| ||
data-orientation | 'horizontal' | 'vertical' | |
| ||
data-disabled | ||
| ||
Thumbmedia-slider-thumb
Draggable handle for setting the slider value. Receives focus and handles keyboard interaction.
Props
| Prop | Type | Default | Details |
|---|---|---|---|
className | string | ((state: SliderState) => string | undefined) | — | |
| |||
render | ReactElement | ((props: HTMLProps, state: SliderState) => ReactElement | null) | — | |
| |||
style | CSSProperties | ((state: SliderState) => CSSProperties | undefined) | — | |
| |||
Data attributes
| Attribute | Type | Details |
|---|---|---|
data-dragging | ||
| ||
data-pointing | ||
| ||
data-interactive | ||
| ||
data-orientation | 'horizontal' | 'vertical' | |
| ||
data-disabled | ||
| ||
Thumbnailmedia-slider-thumbnail
Props
| Prop | Type | Default | Details |
|---|---|---|---|
className | string | ((state: ThumbnailCore.State) => string | undefined) | — | |
| |||
render | ReactElement | ((props: HTMLProps, state: ThumbnailCore.State) => ReactElement | null) | — | |
| |||
style | CSSProperties | ((state: ThumbnailCore.State) => CSSProperties | undefined) | — | |
| |||
thumbnails | ThumbnailImage[] | — | |
| |||
Trackmedia-slider-track
Contains the slider's visual track and interactive hit zone.
Props
| Prop | Type | Default | Details |
|---|---|---|---|
className | string | ((state: SliderState) => string | undefined) | — | |
| |||
render | ReactElement | ((props: HTMLProps, state: SliderState) => ReactElement | null) | — | |
| |||
style | CSSProperties | ((state: SliderState) => CSSProperties | undefined) | — | |
| |||
Valuemedia-slider-value
Displays a formatted text representation of the slider value. Renders an <output> element.
Props
| Prop | Type | Default | Details |
|---|---|---|---|
className | string | ((state: SliderState) => string | undefined) | — | |
| |||
format | ((value: number) => string) | — | |
| |||
render | ReactElement | ((props: HTMLProps, state: SliderState) => ReactElement | null) | — | |
| |||
style | CSSProperties | ((state: SliderState) => CSSProperties | undefined) | — | |
| |||
type | 'current' | 'pointer' | — | |
| |||
Data attributes
| Attribute | Type | Details |
|---|---|---|
data-dragging | ||
| ||
data-pointing | ||
| ||
data-interactive | ||
| ||
data-orientation | 'horizontal' | 'vertical' | |
| ||
data-disabled | ||
| ||