Import

import SanityVideo from '../../components/utility/SanityVideo.astro';

Props

PropTypeRequiredDefaultDescription
id string Yes Looks up a brandVideo document by its name field
ratio '16:9' | '1:1' | '3:2' | '4:3' No '16:9' Fixed aspect ratio. Always set (videos carry no dimension metadata) to prevent layout shift
variant 'full' | 'half' | 'third' | 'quarter' No 'full' Width relative to content column
fit 'crop' | 'fill' | 'contain' | 'cover' No 'cover' How the video fills the frame. crop/cover fill and crop overflow; contain letterboxes
alt string No doc altText Accessible label. Falls back to the document's alt text; pass "" for decorative clips
radius 0 | 4 | 6 | 8 | 10 | 12 | 16 | 20 | 24 | 32 No 16 Corner radius token. Defaults to --primitive-radius-16
description string No Centered caption below the video — body-sm, secondary color. Same convention as FigmaImage

Usage

<!-- Basic -->
<SanityVideo id="brand-shapes-hero" alt="Shapes morphing loop" />

<!-- Square, half width -->
<SanityVideo id="brand-shapes-hero" ratio="1:1" variant="half" alt="Shapes loop" />

<!-- With caption -->
<SanityVideo id="brand-shapes-hero" description="Shapes in motion" alt="Shapes loop" />

<!-- Inside Columns -->
<Columns count={2}>
  <SanityVideo id="clip-a" description="First clip" />
  <SanityVideo id="clip-b" description="Second clip" />
</Columns>

Preparing a video

Compress locally first, then upload in Sanity Studio. Strip audio, cap the resolution, and encode both formats. WebM (VP9) is served first; MP4 (H.264) is the universal fallback.

# 1. MP4 (H.264) — universal fallback. +faststart enables progressive playback.
ffmpeg -i input.mov -an -vf "scale='min(1280,iw)':-2" \
  -c:v libx264 -profile:v high -pix_fmt yuv420p -crf 24 -preset slow \
  -movflags +faststart output.mp4

# 2. WebM (VP9) — smaller; served first to modern browsers.
ffmpeg -i input.mov -an -vf "scale='min(1280,iw)':-2" \
  -c:v libvpx-vp9 -crf 32 -b:v 0 -row-mt 1 output.webm

# 3. Poster — first-frame still (required).
ffmpeg -i input.mov -frames:v 1 -q:v 2 poster.jpg
  • -an strips the audio track (clips are always silent).
  • scale='min(1280,iw)':-2 caps width at 1280px and keeps the aspect ratio (never upscales).
  • Higher -crf = smaller file, lower quality. Tune H.264 ~23–28 and VP9 ~30–34 to taste.

Uploading to Sanity

  1. Open Sanity StudioBrand Video → create a new document.
  2. Set Name (ID) to the value you pass as id (lowercase, hyphenated, unique).
  3. Upload the WebM, MP4, and Poster files, fill in Alt Text, and publish.
  4. Reference it in a page: <SanityVideo id="your-name" />.

Preview — fallback (no video uploaded)

When no matching brandVideo document exists, a neutral surface placeholder is shown. Once a clip is uploaded with a matching name, it renders here.

Preview — 2 columns (text + 1:1 video)

<Columns count={2}>
  <TextSection variant="card" heading="Placeholder heading">
    <p>Placeholder copy goes here...</p>
  </TextSection>
  <SanityVideo id="brand-shapes-loop" ratio="1:1" alt="Shapes loop" />
</Columns>

Placeholder heading

This is placeholder copy. It sits next to the video to show how a two-column layout pairs a text block with a square clip. Swap it for real content when you use the component.

The 1:1 frame keeps its square shape and sets the row height; the text column matches it. (Shown here as the placeholder, since no clip is uploaded — a real square video renders in the same box.)

Preview — 3 columns (1:1 videos)

<Columns count={3}>
  <SanityVideo id="clip-a" ratio="1:1" alt="..." />
  <SanityVideo id="clip-b" ratio="1:1" alt="..." />
  <SanityVideo id="clip-c" ratio="1:1" alt="..." />
</Columns>

Three squares in a row, each filling a third of the width. Same ratio → equal height with no cropping.

Preview — 3 columns (4:3 videos)

Preview — 2 columns (text + 4:3 video)

Placeholder heading

This is placeholder copy beside a 4:3 clip. The 4:3 frame keeps its shape and sets the row height; the text column matches it.

Notes

  • Playback is locked: loop + muted + playsinline, no controls, no audio.
  • Clips are lazy: preload="none" plus an IntersectionObserver — a video only downloads and plays once scrolled into view, and pauses when it leaves.
  • Respects prefers-reduced-motion — reduced-motion users see the poster still instead of motion.
  • A poster (first-frame still) is required and always renders first, so there is no blank flash before load.
  • Unlike FigmaImage, there is no Figma sync — videos are uploaded to Sanity directly and looked up by the document's name field.
  • Works inside Columns at any count (2–4). Each clip keeps its aspect ratio and drives the row height — a row of same-ratio clips (e.g. three 1:1 or three 4:3) stays equal height with no cropping.
  • Served from Sanity's file CDN as-uploaded (no transcoding) — which is why compressing well before upload matters.
Page tokens