Video Slider

A custom video player with an animated scrubber, drag-to-seek with pointer capture, hover time preview, and optional edge-stretch rail effect.

Installation

Pro components require registry authentication. Add your Unlumen UI Pro key as UNLUMEN_LICENSE_KEY in your .env.local file and follow the setup guide.

File Structure

video-slider.tsx

Usage

import { VideoSlider } from "@/components/unlumen-ui/video-slider";

export default function Example() {
  return (
    <VideoSlider
      src="/videos/demo.mp4"
      poster="/videos/demo-poster.jpg"
      stretchEffect
    />
  );
}

Disable Edge Stretch

import { VideoSlider } from "@/components/unlumen-ui/video-slider";

export default function Example() {
  return (
    <VideoSlider
      src="/videos/demo.mp4"
      poster="/videos/demo-poster.jpg"
      stretchEffect={false}
      className="max-w-3xl"
    />
  );
}

API Reference

VideoSlider

src
string

Video source URL.

poster?
string

Poster image shown before playback starts.

className?
string

Additional classes applied to the root wrapper element.

stretchEffect?
boolean
true

Enables the elastic rail deformation when dragging beyond left/right bounds.

Notes

  • The component renders a native <video> (playsInline, preload="metadata") plus a custom control surface under it.
  • The rail hit area is h-20 (80px) for easier grabbing on pointer devices.
  • Rail visuals animate from 6px to 52px height on hover/drag, with matching margin compensation to keep the expansion visually centered.
  • Center play/pause control uses two overlapping motion.span layers (Play + Pause) with spring transitions, so icon swaps do not cause layout shift.
  • While playing, a requestAnimationFrame loop continuously syncs currentTime, duration, and progress from the video element.
  • Seeking uses pointer capture (setPointerCapture) and keeps dragging responsive even when the pointer exits the rail bounds.
  • On hover, a floating time preview appears at the cursor-projected position on the rail.
  • currentTime and duration labels are always present and animate their position/opacity/state between idle and expanded modes.
  • When the video ends, playback state is paused and progress remains at 1 (end state).
  • If stretchEffect is enabled, dragging beyond either edge applies an elastic deformation (scale + origin shift + directional offset) and springs back on release.
  • On track resize, ResizeObserver recalculates geometry and repositions thumb/fill to preserve visual sync.

Accessibility

  • The full video area is a semantic <button> for play/pause toggling with dynamic aria-label (Play / Pause).
  • The scrubber remains pointer-first today (drag/hover), with no keyboard timeline interaction yet.
  • For production usage with captions/subtitles, include video text tracks directly on your media source when needed.

Credits

Built by leo. Inspired by Skiper UI's video player 002 component, great work from Guri.

Keep in mind

Most components on this site are inspired by or recreated from existing work across the web. I'm not here to take credit; just to learn, experiment, and sometimes push things a bit further. If something looks familiar and I forgot to mention you, reach out and I'll fix that right away.