Skip to content
FrameworkStyle

TimeSlider

A slider component for seeking through media playback time

Anatomy

<TimeSlider.Root>
  <TimeSlider.Track>
    <TimeSlider.Buffer />
    <TimeSlider.Fill />
  </TimeSlider.Track>
  <TimeSlider.Thumb />
  <TimeSlider.Preview>
    <TimeSlider.Value type="pointer" />
  </TimeSlider.Preview>
</TimeSlider.Root>

Add Chapters to progressively divide the track when a default kind="chapters" text track is available.

<TimeSlider.Chapters
  renderChapter={(props, state) => (
    <div {...props}>
      <TimeSlider.Track>
        <TimeSlider.Buffer />
        <TimeSlider.Fill />
      </TimeSlider.Track>
    </div>
  )}
>
  <TimeSlider.Track>
    <TimeSlider.Buffer />
    <TimeSlider.Fill />
  </TimeSlider.Track>
</TimeSlider.Chapters>

children render as the regular slider track until authored chapters are available. renderChapter receives the attributes, styles, and state for each normalized chapter.

Behavior

Displays and controls the current playback position. Dragging the slider seeks the media. The fill level reflects currentTime / duration as a percentage, and the buffer level shows how much media has been buffered.

Value changes during drag are throttled via the changeThrottle prop (default 100ms) using a leading+trailing throttle to keep the UI responsive without overwhelming the media element.

Chapter cues are normalized into contiguous ranges. Any uncovered time becomes a gap range, so track geometry still spans the full duration. Gap ranges receive the same geometry styles but cannot be highlighted and have no chapter title.

Styling

Use CSS custom properties to style the fill, pointer, and buffer levels:

React renders a <div> element. Add a className to style it:

.time-slider::before {
  width: var(--media-slider-fill);
}

Use data-seeking to style during active seek operations:

.time-slider[data-seeking] {
  opacity: 0.8;
}

Each rendered chapter receives data-active while playback is inside it and data-highlighted while the pointer is inside an authored chapter. Its geometry and local progress are exposed through:

  • --media-slider-chapter-start
  • --media-slider-chapter-end
  • --media-slider-chapter-width
  • --media-slider-chapter-fill
  • --media-slider-chapter-buffer

The generated fill and buffer parts also inherit the slider-level --media-slider-fill and --media-slider-buffer values. This allows every chapter to render full-width progress planes and use its chapter bounds as a clip, keeping progress transitions continuous across segments.

Accessibility

Renders with role="slider" and an automatic aria-label that resolves to “Seek” from the active locale. Override with the label prop. Keyboard controls:

  • Arrow Left / Arrow Right: step by step increment
  • Page Up / Page Down: step by largeStep increment
  • Home: seek to start
  • End: seek to end

ChapterTitle follows the pointer during pointer interaction. While the slider has keyboard focus, it follows the current playback position and announces chapter changes.

Examples

Nest sub-components for full control over the slider’s DOM structure. This example includes a track, fill bar, buffer indicator, draggable thumb, and a tooltip that shows the pointed-at time.

0:00
import { createPlayer, TimeSlider } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';

const Player = createPlayer({ features: videoFeatures });

export default function WithParts() {
  return (
    <Player.Provider>
      <Player.Container className="media-container">
        <Video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" autoPlay muted playsInline loop />
        <TimeSlider.Root className="media-time-slider">
          <TimeSlider.Track className="media-slider-track">
            <TimeSlider.Buffer className="media-slider-buffer" />
            <TimeSlider.Fill className="media-slider-fill" />
          </TimeSlider.Track>
          <TimeSlider.Thumb className="media-slider-thumb" />
          <TimeSlider.Value type="pointer" className="media-slider-value" />
        </TimeSlider.Root>
      </Player.Container>
    </Player.Provider>
  );
}

With chapters

Add a default chapters track, then provide the markup used for each chapter. The deliberate one-minute gap in this example remains visible as an unlabelled, non-highlightable segment.

0:00
import { createPlayer, TimeSlider } from '@videojs/react';
import { Video, videoFeatures } from '@videojs/react/video';

const Player = createPlayer({ features: videoFeatures });

export default function WithChapters() {
  return (
    <Player.Provider>
      <Player.Container className="media-container">
        <Video src="https://stream.mux.com/BV3YZtogl89mg9VcNBhhnHm02Y34zI1nlMuMQfAbl3dM/highest.mp4" autoPlay muted playsInline loop crossOrigin="anonymous">
          <track kind="chapters" src="/docs/demos/time-slider/chapters.vtt" srcLang="en" default />
        </Video>
        <TimeSlider.Root className="media-time-slider">
          <TimeSlider.Chapters
            className="media-slider-chapters"
            renderChapter={(props) => (
              <div {...props} className="media-slider-chapter">
                <TimeSlider.Track className="media-slider-track">
                  <TimeSlider.Buffer className="media-slider-buffer" />
                  <TimeSlider.Fill className="media-slider-fill" />
                </TimeSlider.Track>
              </div>
            )}
          >
            <TimeSlider.Track className="media-slider-track">
              <TimeSlider.Buffer className="media-slider-buffer" />
              <TimeSlider.Fill className="media-slider-fill" />
            </TimeSlider.Track>
          </TimeSlider.Chapters>
          <TimeSlider.Thumb className="media-slider-thumb" />
          <TimeSlider.Preview className="media-slider-preview">
            <TimeSlider.ChapterTitle className="media-slider-chapter-title" />
            <TimeSlider.Value type="pointer" />
          </TimeSlider.Preview>
        </TimeSlider.Root>
      </Player.Container>
    </Player.Provider>
  );
}

API Reference

Root

Props

PropTypeDefaultDetails
changeThrottlenumber100
disabledboolean
labelText | string | Text) | function''
largeStepnumber
maxnumber
minnumber
orientation'horizontal' | 'vertical'
pauseOnDragbooleanfalse
stepnumber
thumbAlignment'center' | 'edge'
valuenumber

State

State is accessible via the render, className, and style props.

PropertyTypeDetails
bufferPercentnumber
valuenumber
fillPercentnumber
pointerPercentnumber
draggingboolean
pointingboolean
interactiveboolean
orientation'horizontal' | 'vertical'
disabledboolean
thumbAlignment'center' | 'edge'
currentTimenumber
seekingboolean
durationnumber

Data attributes

AttributeTypeDetails
data-seeking

CSS custom properties

VariableDetails
--media-slider-fill
--media-slider-pointer
--media-slider-buffer

ChapterTitle

Displays the chapter title at the current pointer or keyboard position.

Chapters

Renders normalized chapter ranges as consumer-owned light DOM.

Props

PropTypeDefaultDetails
renderChapter(props: Omit<HTMLProps<HTMLElement>, "ref">, state: TimeSliderChapterState) => ReactElement<unknown, string | JSXElementConstructor<any>>

Buffer

Displays the buffered range on the slider track.

Fill

Displays the filled portion from start to the current value.

Preview

Positioning container for preview content that tracks the pointer along the slider.

Props

PropTypeDefaultDetails
overflowSliderPreviewOverflow

Data attributes

AttributeTypeDetails
data-dragging
data-pointing
data-interactive
data-orientation'horizontal' | 'vertical'
data-disabled

Thumb

Draggable handle for setting the slider value. Receives focus and handles keyboard interaction.

Data attributes

AttributeTypeDetails
data-dragging
data-pointing
data-interactive
data-orientation'horizontal' | 'vertical'
data-disabled

Track

Contains the slider's visual track and interactive hit zone.

Value

Displays a formatted text representation of the slider value. Renders an <output> element.

Props

PropTypeDefaultDetails
format((value: number) => string)
type"current" | "pointer"

Data attributes

AttributeTypeDetails
data-dragging
data-pointing
data-interactive
data-orientation'horizontal' | 'vertical'
data-disabled