Command Palette

Search for a command to run...

Segmented Timeline Strip

Multi-clip timeline overview built by composing one thumbnail-strip per segment with DOM chrome (active border, dim mask, label, playhead, total duration badge) and click-to-seek.

A horizontal timeline overview that concatenates multiple video segments end-to-end. Each segment is rendered by a thumbnail-strip; the wrapper layers DOM chrome on top — active-segment border, dim mask, segment label, playhead, total duration badge — and translates click-to-seek into per-segment coordinates.

Segment widths are proportional to duration. The whole strip fills its container; no horizontal scrolling. Multiple segments may share one VideoThumbnailCache (different startOffsets into the same video) or each carry its own cache (multi-clip projects).

Installation

Pulls thumbnail-strip and video-thumbnail-cache (and transitively mediabunny) automatically.

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/segmented-timeline-strip

Usage

import {
  SegmentedTimelineStrip,
  type TimelineSegment,
} from "@/components/segmented-timeline-strip";
import { VideoThumbnailCache } from "@/lib/video-thumbnail-cache";
const cache = await VideoThumbnailCache.fromUrl(videoUrl)
const { duration } = cache.getMetadata()!
const segDur = duration / 5

const segments: TimelineSegment[] = Array.from({ length: 5 }, (_, i) => ({
  id: i,
  cache,
  duration: segDur,
  startOffset: i * segDur,
  label: String(i + 1),
}))

<SegmentedTimelineStrip
  segments={segments}
  currentIndex={currentIndex}
  currentTime={video.currentTime - currentIndex * segDur}
  onSeek={({ segmentIndex, timeWithinSegment }) => {
    video.currentTime = segmentIndex * segDur + timeWithinSegment
  }}
/>

To wire a video element's timeupdate to the playhead, lift currentTime into React state and feed the strip the segment-local time (subtract the current segment's startOffset).

Examples

Wired to a video player

Each segment can instead carry its own VideoThumbnailCache (a multi-clip project). Lift currentTime from the <video>, feed the strip the segment-local time, and on onSeek swap the video src when crossing a clip boundary — here two real clips that auto-advance on ended.

Props

PropTypeDefault
segments
TimelineSegment[]
-
currentIndex
number
-
currentTime
number
0
onSeek
({ segmentIndex, timeWithinSegment }) => void
-
height
number
64
tileWidth
number
36
showTotalDurationBadge
boolean
true
autoFallback
boolean
true
placeholderColor
string
"#111827"
className
string
-
style
React.CSSProperties
-