Video
Import
Section titled “Import”import { Video } from '@delightstack/components';import type { Source, Track } from '@delightstack/components';Basic Usage
Section titled “Basic Usage”View code
<script> import { Video } from '@delightstack/components';
</script>
<Video src="/videos/intro.mp4" poster="/videos/intro-poster.jpg" />Examples
Section titled “Examples”Multiple Quality Sources
Section titled “Multiple Quality Sources”Provide multiple sources with resolution labels for quality switching.
<script> import { Video } from '@delightstack/components'; import type { Source } from '@delightstack/components';
const sources: Source[] = [ { src: '/videos/demo-1080.mp4', type: 'video/mp4', size: 1080 }, { src: '/videos/demo-720.mp4', type: 'video/mp4', size: 720 }, { src: '/videos/demo-480.mp4', type: 'video/mp4', size: 480 }, ];</script>
<Video src={sources} poster="/videos/demo-poster.jpg" />HLS Streaming
Section titled “HLS Streaming”Point src at an .m3u8 playlist (or pass a source with type application/vnd.apple.mpegurl) and the player handles HLS automatically. Browsers with native HLS support (Safari, iOS) play it directly; everywhere else the player lazy-loads hls.js only when an HLS source is detected. The custom controls — including a manifest-driven quality menu with an Auto option — are used in both cases.
hls.js is an optional peer dependency. Install it for HLS support in non-Safari browsers:
pnpm add hls.jsView code
<Video src="https://stream.example.com/playlist.m3u8" />With Captions
Section titled “With Captions”Add subtitle or caption tracks.
<script> import { Video } from '@delightstack/components'; import type { Track } from '@delightstack/components';
const captions: Track[] = [ { kind: 'subtitles', src: '/subs/en.vtt', srclang: 'en', label: 'English', default: true }, { kind: 'subtitles', src: '/subs/es.vtt', srclang: 'es', label: 'Spanish' }, ];</script>
<Video src="/videos/lecture.mp4" {captions} />Autoplay Muted
Section titled “Autoplay Muted”Auto-playing videos must be muted to comply with browser policies.
Autoplay requires muted ; browsers block autoplay-with-sound until the user interacts.
View code
<Video src="/videos/background.mp4" autoplay muted loop controls={false} />Accessing the Player
Section titled “Accessing the Player”Bind the native HTMLVideoElement for programmatic control.
<script> import { Video } from '@delightstack/components';
let player = $state<HTMLVideoElement>();</script>
<Video src="/videos/clip.mp4" bind:player onready={({ player: p }) => { console.log('Video ready, duration:', p.duration);}} />
<button onclick={() => player?.play()}>Play</button>Skeleton Loading
Section titled “Skeleton Loading”Use skeleton while the source is still being resolved (e.g. fetched async).
It fills the player with a placeholder background, skeleton controls, and a
shimmer sweep. Providing a src automatically disables the skeleton — so the
shimmer never sits on top of a real poster or video.
View code
<script> import { Video } from '@delightstack/components';
// no src yet → skeleton; set it once it resolves let src = $state('');</script>
<Video {src} skeleton={!src} />| Prop | Type | Default | Description |
|---|---|---|---|
src | string | Source[] | required | Video source URL or array of sources with quality options. .m3u8 URLs (or sources typed application/vnd.apple.mpegurl) are played as HLS |
poster | string | undefined | Poster image URL |
autoplay | boolean | false | Auto-play (requires muted for most browsers) |
muted | boolean | false | Start muted ($bindable) |
loop | boolean | false | Loop playback |
controls | boolean | true | Show custom controls |
aspect_ratio | string | '16/9' | CSS aspect ratio |
preload | 'auto' | 'metadata' | 'none' | 'metadata' | Preload behavior |
captions | Track[] | [] | Caption/subtitle tracks |
skeleton | boolean | false | Show loading skeleton while no src is known yet (providing a src disables it) |
id | string | auto | Element ID |
class | string | '' | Additional CSS classes |
element | HTMLElement | undefined | Bindable reference to the root element ($bindable) |
player | HTMLVideoElement | undefined | Bindable reference to the video element ($bindable) |
Events
Section titled “Events”| Event | Detail | Description |
|---|---|---|
onplay | none | Fires when playback starts |
onpause | none | Fires when playback pauses |
onended | none | Fires when playback ends |
ontimeupdate | { currentTime: number, duration: number } | Fires on time update |
onerror | { error: MediaError } | Fires when a playback error occurs |
onenterfullscreen | none | Fires when entering fullscreen |
onexitfullscreen | none | Fires when exiting fullscreen |
onenterpip | none | Fires when entering picture-in-picture |
onexitpip | none | Fires when exiting picture-in-picture |
onready | { player: HTMLVideoElement } | Fires when the video element is ready |
Source
Section titled “Source”interface Source { src: string; type: string; size?: number;}interface Track { kind: 'captions' | 'subtitles'; src: string; srclang: string; label: string; default?: boolean;}Accessibility
Section titled “Accessibility”- Container has
role="group"witharia-label="Video player" tabindex="0"for keyboard focus- Space / K toggles play/pause
- Arrow Left / Right seeks backward/forward 10 seconds
- Arrow Up / Down adjusts volume
- M toggles mute, F toggles fullscreen, C toggles captions
- Progress bar uses
role="slider"witharia-label="Seek"andaria-valuenow/aria-valuemax - Volume slider uses
role="slider"witharia-label="Volume" - All control buttons have descriptive
aria-labelattributes - Quality and speed menus use
aria-haspopupandaria-expanded - Controls auto-hide during playback and reappear on mouse/touch/keyboard activity
- Focus-visible outlines on all interactive elements