Media & time

Video player

A video player with its own controls — progressive files (MP4, WebM), no streaming.

Client componentSource

Import

import { VideoPlayer } from '@the_viveksingh/vivek-ui'

One bar, real controls

Every control is a real button or range with a name, and the shortcuts are the ones every player taught people: Space/K play, arrows seek and change volume, J/L jump ten seconds, M mutes, F goes fullscreen. Controls fade while the pointer rests and come back on any key or focus - faded, never removed.

Paused. Space or K plays, arrows seek and change volume, M mutes, F goes fullscreen. Controls fade while the pointer rests and return on any key or focus.

<VideoPlayer
  label="Product tour"
  src="/media/tour.mp4"
  poster="/media/tour-poster.jpg"
  onEnded={() => track('tour_watched')}
/>

Sources, captions and speeds

Several sources let the browser pick the first it can decode. WebVTT tracks add a Captions toggle (C); the default track starts showing. rates sets the speeds offered - an empty array hides the control. Cross-origin captions need crossOrigin on the element, which videoProps passes through.

Paused. Space or K plays, arrows seek and change volume, M mutes, F goes fullscreen. Controls fade while the pointer rests and return on any key or focus.

<VideoPlayer
  label="Keynote"
  src={[
    { src: '/media/keynote.webm', type: 'video/webm' },
    { src: '/media/keynote.mp4', type: 'video/mp4' },
  ]}
  tracks={[
    { src: '/media/keynote.en.vtt', srclang: 'en', label: 'English', default: true },
    { src: '/media/keynote.hi.vtt', srclang: 'hi', label: 'हिन्दी' },
  ]}
  rates={[1, 1.5, 2]}
  ratio={4 / 3}
  videoProps={{ crossOrigin: 'anonymous' }}
/>

Props

Generated from the package's own type declarations, so this table cannot drift from the code.

Props for VideoPlayer
PropTypeDefaultDescription
srcstring | VideoSource[]—One URL, or several sources the browser tries **in order**. List the most widely supported format first (MP4/H.264, then WebM): Safari holds the document's `load` event while it works through a format it cannot decode, so a WebM-first list makes the whole page appear to hang there.
posterstring—Frame shown before playback. **A raster image (JPEG, PNG, WebP)** — Safari ignores an SVG poster, and the document's `load` event never fires while it waits for one.
tracksVideoTrack[]—WebVTT text tracks. The first `default` one (else the first) is the captions track.
labelstring—Accessible name of the player. Default `'Video player'`.
rationumber—Width / height. Default `16 / 9`.
autoPlayboolean——
mutedboolean—Start muted — required by every browser for `autoPlay` to actually play.
loopboolean——
playsInlineboolean—Default `true`: play in place on phones instead of taking over the screen.
preload'none' | 'metadata' | 'auto'——
ratesnumber[]—Playback speeds offered. Default `[0.5, 1, 1.25, 1.5, 2]`; `[]` hides the control.
seekStepnumber—Seconds moved by ArrowLeft / ArrowRight. Default `5`.
hideControlsAfternumber—Hide the controls this many ms after the pointer rests while playing. `0` never hides. Default `2500`.
errorMessageReactNode—Shown when the media fails to load or decode.
videoRefRef<HTMLVideoElement>—The `<video>` element, for anything the props do not cover.
videoPropsOmit<VideoHTMLAttributes<HTMLVideoElement>, 'src' | 'poster' | 'controls' | 'autoPlay' | 'muted' | 'loop' | 'playsInline' | 'preload'>—Extra attributes for the `<video>` — `crossOrigin` for cross-origin captions, for one.
onPlayingChange(playing: boolean) => void——
onTimeChange(currentTime: number, duration: number) => void——
onEnded() => void——
onError() => void——

Every remaining prop is spread onto the root element, so all standard HTML and ARIA attributes work. className and style are merged with the library's own, never replaced, and the ref forwards to the root DOM node.

Rendering