Primitives to manage HTML video playback.
| Stage | Category | Version | Last Updated | Demo |
|---|---|---|---|---|
| 3 | Display & Media | 1.0.0-next.3 (next) | Aug 12, 2026 | Demo → |
npm i @solid-primitives/video@nextLayered primitives for managing HTML video playback. The make* variants are non-reactive and require no Solid owner. The create* variants integrate with Solid's reactive system — createVideo covers essential playback state, and createVideoPlayer extends it with the full control surface.
How to use it
makeVideo
Creates a raw HTMLVideoElement with optional event handlers and initial configuration. No Solid owner required.
const [player, cleanup] = makeVideo("clip.mp4", {}, { muted: true, loop: true });cleanup();function makeVideo( src: VideoSource | HTMLVideoElement, handlers?: VideoEventHandlers, options?: VideoOptions,): [player: HTMLVideoElement, cleanup: VoidFunction];makeVideoPlayer
Wraps makeVideo with imperative playback controls. No Solid owner required.
const [{ play, pause, seek, setVolume, setMuted, setPlaybackRate, setLoop }, cleanup] = makeVideoPlayer("clip.mp4");
await play();seek(30);setPlaybackRate(1.5);setLoop(true);cleanup();function makeVideoPlayer( src: VideoSource | HTMLVideoElement, handlers?: VideoEventHandlers, options?: VideoOptions,): [controls: VideoControls, cleanup: VoidFunction];createVideo
Essential reactive playback state: playing, currentTime, ended, seeking, error, and an async duration that suspends until metadata is loaded.
const video = createVideo("clip.mp4");// or with a reactive source:const video = createVideo(() => selectedUrl());
video.playing(); // boolean — true while actively playingvideo.setPlaying(true); // playsvideo.currentTime(); // secondsvideo.seek(30);video.ended(); // booleanvideo.seeking(); // boolean — true while scrubbingvideo.error(); // MediaError | nullThe duration accessor throws NotReadyError until video metadata has loaded, integrating with Solid 2.0's <Loading> boundary:
<Loading fallback="Loading…"> <span>{video.duration()}s</span></Loading>function createVideo(src: VideoSource | Accessor<VideoSource>, options?: VideoOptions): VideoReturn;createVideoPlayer
Extends createVideo with the full control surface: volume, muted, playback rate, loop, buffering state, and dimensions. Accepts all VideoOptions plus volume and playbackRate initial values.
const video = createVideoPlayer("clip.mp4", { muted: true, volume: 0.8, playbackRate: 1,});
// All fields from createVideo, plus:video.volume(); // 0–1video.setVolume(0.5);video.muted(); // booleanvideo.setMuted(true);video.playbackRate(); // numbervideo.setPlaybackRate(1.5);video.loop(); // booleanvideo.setLoop(true);video.buffered(); // TimeRanges | undefinedvideo.readyState(); // 0–4video.videoWidth(); // intrinsic pixel widthvideo.videoHeight(); // intrinsic pixel heightFullscreen is intentionally omitted — use the dedicated
@solid-primitives/fullscreenprimitive to manage fullscreen state and attach it tovideo.player.
function createVideoPlayer( src: VideoSource | Accessor<VideoSource>, options?: VideoControlsOptions,): VideoControlsReturn;makeVideoFrameCallback
Wraps HTMLVideoElement.requestVideoFrameCallback, which fires once per displayed video frame instead of once per display refresh — it stops naturally while the video is paused, and the metadata argument (mediaTime, presentedFrames, etc.) lets you sync work to actual playback instead of wall-clock time. No Solid owner required.
const [player, cleanup] = makeVideo("clip.mp4");const [running, start, stop] = makeVideoFrameCallback(player, (now, metadata) => { draw(metadata.mediaTime);});start();stop();cleanup();function makeVideoFrameCallback( video: HTMLVideoElement, callback: VideoFrameRequestCallback,): [running: () => boolean, start: VoidFunction, stop: VoidFunction];createVideoFrameCallback
Reactive version of makeVideoFrameCallback — takes an accessor for the video element, so it re-attaches whenever the element changes and stops cleanly when it becomes undefined. running is a Solid signal, and playback is automatically stopped onCleanup.
const video = createVideo("clip.mp4");const [running, start, stop] = createVideoFrameCallback( () => video.player, (now, metadata) => { console.log(metadata.presentedFrames); },);start();function createVideoFrameCallback( el: Accessor<HTMLVideoElement | undefined>, callback: VideoFrameRequestCallback,): [running: Accessor<boolean>, start: VoidFunction, stop: VoidFunction];Types
type VideoSource = string | undefined | MediaProvider;
type VideoOptions = { autoPlay?: boolean; loop?: boolean; muted?: boolean; preload?: "" | "none" | "metadata" | "auto";};
type VideoControlsOptions = VideoOptions & { volume?: number; playbackRate?: number;};
type VideoReturn = { player: HTMLVideoElement; playing: Accessor<boolean>; setPlaying: (v: boolean) => void; currentTime: Accessor<number>; seek: (time: number) => void; ended: Accessor<boolean>; seeking: Accessor<boolean>; error: Accessor<MediaError | null>; duration: Accessor<number>; // throws NotReadyError until loaded};
type VideoControlsReturn = VideoReturn & { volume: Accessor<number>; setVolume: (v: number) => void; muted: Accessor<boolean>; setMuted: (v: boolean) => void; playbackRate: Accessor<number>; setPlaybackRate: (rate: number) => void; loop: Accessor<boolean>; setLoop: (v: boolean) => void; buffered: Accessor<TimeRanges | undefined>; readyState: Accessor<number>; videoWidth: Accessor<number>; videoHeight: Accessor<number>;};Changelog
See CHANGELOG.md