Command Palette

Search for a command to run...

Timeline Playhead

A draggable timeline playhead: a vertical line with a top knob, positioned by time (currentTime × pixelsPerSecond × zoom, the same basis as timeline-ruler) and scrubbable. Drag it to seek; it scrolls with the tracks it sits over. Spans whatever container it is placed in. The playback clock and click-to-seek on the tracks are left to the consumer.

The playhead — the vertical cursor that marks the current time on an editor's timeline. It positions itself by time (currentTime × pixelsPerSecond × zoom, the same basis as timeline-ruler) and fills whatever container it sits in, so it spans the ruler and every track beneath it. Drag the line or its knob to scrub; because it lives inside the scrolled content, it scrolls in lockstep with the tracks.

It is controlled: render it at currentTime and update from onSeek. The handles are focusable, so arrow keys nudge frame by frame.

It is only the cursor. The playback clock and click-to-seek on the ruler or tracks are application concerns — the consumer wires the container's click to onSeek and feeds the playback time back in. The component draws a draggable line, nothing more.

Installation

One-time setup: add the @ikui registry to your components.json.

components.json
{
  "registries": {
    "@ikui": "https://ik-ui.pages.dev/r/{name}.json"
  }
}

Then install the component:

pnpm dlx shadcn@latest add @ikui/timeline-playhead

Usage

import { TimelinePlayhead } from "@/components/timeline-playhead";
const [time, setTime] = useState(0)

<div style={{ position: "relative", width: duration * 50 }}>
  <TimelineRuler duration={duration} />
  {/* …tracks… */}
  <TimelinePlayhead
    currentTime={time}
    duration={duration}
    onSeek={setTime}
  />
</div>

The playhead is absolutely positioned and measures pointer scrubs against its parent, so place it inside the time-scaled content — a position: relative container of width duration × pixelsPerSecond × zoom that also holds the ruler and tracks. It then spans them all and stays aligned to the ticks.

Examples

Across the tracks

The playhead sitting over a timeline-ruler and an audio clip (timeline-element + audio-waveform), spanning both. Click anywhere to move it, or drag the knob to scrub — the consumer turns the container's click into onSeek; the playhead handles its own drag.

Props

PropTypeDefault
currentTime
number
-
duration
number
-
pixelsPerSecond
number
50
zoom
number
1
step
number
0.1
color
string
"currentColor"
onSeek
(time: number) => void
-
className
string
-
style
React.CSSProperties
-