Video generation
A video job in a conversation: queue position, progress with the time left, a poster frame revealed as it renders, then a compact player with the clip's length and resolution.
On this page
A clip waits in the queue, renders with time left, then plays. The video is recorded in your browser.
The demo clip is drawn on a canvas and recorded in your browser while the job renders, so the page ships no video file.
Installation
Usage
import { VideoGeneration } from "@/components/ui/chatcn/video-generation"
<VideoGeneration
prompt={job.prompt}
status={job.status}
queuePosition={job.queuePosition}
progress={job.progress}
posterSrc={job.posterUrl}
src={job.videoUrl}
duration={6}
resolution="1080p"
fps={24}
audio={false}
onCancel={cancel}
/>Pass etaMs when the provider gives one. Otherwise the time left is measured from how fast progress moves: it waits for enough signal, smooths the rate, and counts down between updates. While queued, etaMs reads as the expected wait. VideoPlayer is exported on its own for clips that are not generated.
Examples
Every state
Queued with an expected wait, generating with a poster, done, failed and content blocked.
Props
VideoGeneration
| Prop | Type | Notes |
|---|---|---|
prompt | string | Shown with a copy button |
status | "queued" | "generating" | "done" | "failed" | "blocked" | The player appears when done and src is set |
progress | number | 0 to 100 |
queuePosition | number | 1 means next |
etaMs | number | Time left, or the expected wait while queued |
posterSrc | string | Revealed while rendering, then the player's poster |
src | string | The finished clip |
duration | number | Seconds. Also the fallback for files without one |
resolution | string | { width, height } | "1080p" or a size |
fps, audio | number, boolean | Shown in the summary line |
aspectRatio | "W:H" | number | Defaults to "16:9" |
settings | ReactNode[] | Extra facts, such as the model |
actions | ReactNode | Shown when done, for example Extend or Remix |
onRetry, onCancel, onEditPrompt, onDownload | () => void | |
autoPlay, loop | boolean | Passed to the player |
VideoPlayer
| Prop | Type | Notes |
|---|---|---|
src, poster | string | |
title | string | Names the player for screen readers |
duration | number | Used until the file reports its own |
aspectRatio | number | Default 16 / 9 |
autoPlay, loop, defaultMuted | boolean |
Keyboard
| Key | Action |
|---|---|
Space, K | Play or pause |
← → | Seek 5 s. Mirrored in RTL. On the scrubber, arrows step it |
M | Mute or unmute |
F | Full screen |
Accessibility
- While rendering, the status line is a
progressbarwhose value text includes the time left. - The scrubber is a native range input with spoken times, for example 2 seconds of 6 seconds.
- Controls stay visible while paused or focused, and every icon button has a label.
- Status changes are announced once in a polite live region.
Motion
- The poster frame uses the same scan reveal as image generation, so a still and a clip feel like one family.
- The control bar fades in 150 ms and hides 2 s after the pointer stops while playing. Keyboard focus shows it instantly.
- Play and pause swap with a 150 ms blur crossfade. The scrubber follows the playhead every frame, not four times a second.