Video Player
Native HTML5 video wrapped in the shared media surface. Controls-on for a standard player, controls-off for a chromeless viewer.
Installation
import { VideoPlayer } from "@gradeui/ui"Player mode
Viewer mode (chromeless)
No controls, autoplay, muted, loops — the pattern for hero / background video.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| src | string | - | Video source URL. |
| controls | boolean | true | Show native playback controls. Set false for chromeless background video. |
| autoPlay | boolean | false | Autoplay on mount. Respects reduced-motion. |
| loop | boolean | false | Loop at end of video. |
| muted | boolean | autoPlay | Mute audio. Required true if autoPlay (browser restriction). |
| pauseOffscreen | boolean | true | Pause when the surface leaves the viewport. |
| aspect | "video" | "square" | "portrait" | "wide" | "auto" | "video" | Aspect ratio of the surface. |
| radius | "none" | "sm" | "md" | "lg" | "xl" | "lg" | Corner radius — driven by `--rds-media-radius` CSS var. |
| objectFit | "cover" | "contain" | "fill" | "cover" | How the video sits inside the surface. |
| poster | string | - | Poster image shown before playback. |
| playbackRate | number | 1 | Playback speed. |
Sidecar
The Markdown sidecar Studio (and the Grade MCP server, when it ships) reads to understand this component — frontmatter, when- to-use guidance, and canonical examples. Authored once at packages/ui/components/ui/video-player.md and shipped inside the published @gradeui/ui tarball.
---
name: VideoPlayer
import: "@gradeui/ui"
props:
- src: string — video URL
- controls?: boolean (default true) — show native controls; false for chromeless hero/background video
- autoPlay?: boolean (default false) — forces muted=true (browser restriction)
- loop?: boolean (default false)
- muted?: boolean (default = autoPlay)
- pauseOffscreen?: boolean (default true) — pause when scrolled out of viewport
- aspect?: "video" | "square" | "portrait" | "wide" | "auto" (default "video")
- radius?: "none" | "sm" | "md" | "lg" | "xl" (default "lg") — driven by `--rds-media-radius`
- objectFit?: "cover" | "contain" | "fill" (default "cover")
- poster?: string — image shown before playback. Always rendered as a `loading="lazy"` `<img>` overlay (not the native `poster` attribute, which fetches eagerly).
- playbackRate?: number (default 1)
when_to_use: HTML5 video wrapped in the shared media surface. Controls-on for a standard player, controls-off (+ autoplay/muted/loop) for hero / background video. Prefer Rive for anything interactive, Three Scene for shader backgrounds.
composes_with: [MediaSurface (internal), Card (wrap for thumbnail grids)]
aliases: [video, mp4, movie, webm, clip, video view, av player, react native video, video element]
notes: Poster images are always lazy-loaded. We don't use the native `<video poster>` attribute because browsers fetch it eagerly even when the surface is off-screen, which wastes the offscreen-pause savings. Instead we render `<img loading="lazy" decoding="async">` layered over the video, then fade it out on `onPlaying`. When no `src` is given nothing renders — always pass a URL.
---
```jsx
<VideoPlayer src="/sample.mp4" poster="/movie-poster.jpg" controls />
// Chromeless hero video
<VideoPlayer src="/sample.mp4" controls={false} autoPlay loop muted aspect="wide" />
```