Video thumb without poster? Paint first frame

PDP gallery thumbs for Studio videos without a poster show blank chrome. I preload metadata and seek a hair past zero so the first decoded frame paints.

Sander Korf3 min read

The same Centra fashion storefront lets merchandisers drop a product video into the PDP gallery from Sanity Studio. Each video can carry an optional poster still. Shoppers browse thumbs before they open the lightbox. When the poster field is empty, my thumb was a grey rectangle with native video controls peeking through.

Requiring a poster in Studio validation would block publish on busy days. Shipping empty thumbs trains shoppers to think the slide is broken. I needed a fallback that paints something honest without faking a CMS asset.

A Studio poster is the optional still on the Sanity video object. When it exists, the browser uses it like any image thumb. When it does not, preload="metadata" plus a tiny seek after loadedmetadata decodes the first frame into the element.

Empty chrome until someone presses play

Without a poster, <video> draws its own empty frame until playback starts. Gallery grids look like missing media. Lightbox navigation feels worse because every video slide matches the broken pattern.

I tried preload="none". Thumbs stayed blank longer. preload="auto" pulled whole files for items nobody opened. Metadata is the middle path: enough bytes to learn duration and dimensions, not the entire clip.

Lightbox slides reuse the same component at a larger size. A missing poster there is even more obvious because the shopper already committed to viewing media. The thumb strip is where I noticed it first: six tiles in a row, five showing fabric, one showing grey chrome.

Gallery thumb without a Studio poster

Merchandisers sometimes upload a product video and skip the optional poster in Sanity. Without a still, the thumb is empty chrome until someone hits play. Toggle poster to compare.

No poster yet: waiting for metadata…

Seek a hair past zero

Some engines will not paint frame zero until the playhead moves. paintFirstVideoFrame checks currentTime, then sets 0.001 inside try/catch for environments where seeking is not ready yet (including jsdom in unit tests).

export function paintFirstVideoFrame(node: HTMLVideoElement) {
	if (node.currentTime > 0) return;
	try {
		node.currentTime = 0.001;
	} catch {
		/* not seekable yet */
	}
}

Wire it on loadedMetadata only when poster is missing. If Studio supplied a poster, keep the normal path and let the still do the work.

function onLoadedMetadata(event: React.SyntheticEvent<HTMLVideoElement>, poster?: string) {
	if (poster) return;
	paintFirstVideoFrame(event.currentTarget);
}

Toggle poster off in the demo and the thumb fills after metadata. Toggle it on and the Studio still wins immediately.

When I still want a real poster

The seek trick is a safety net, not a merchandising workflow. Art-directed posters crop better and load faster than decoding video frames on a dense PLP grid. I kept Studio poster optional so campaigns are not blocked, and I nudge editors toward a still when the hero frame matters.

Accessibility stays the same: alt text on the surrounding control, no autoplay in the thumb strip. The first frame is decorative until the shopper opens the lightbox.

Related-product rails and search cards that reuse gallery thumbs inherit the same fallback. I did not fork a second component for listing density. One helper, one metadata handler, poster branch when Studio supplied the still.

Centra sync does not generate posters from video files automatically. That would be a nice pipeline someday. Until then, editors skip the field and shoppers still deserve a tile that looks intentional.

A blank thumb reads like a bug even when the file is fine. Metadata plus a millisecond seek is cheap insurance.


Happy coding! Sander