Centra storefront, Sanity emailSettings, React Email templates, Klaviyo CODE HTML on push. Marketing wanted a hero clip in the weekly campaign. <video> looked fine in the preview tab. In Gmail it was a blank rectangle. Outlook laughed harder.
Email clients do not run <video>. Some strip the tag. Some leave an empty box. Autoplay was never on the table. The clip still had to feel tappable: poster frame, play affordance, land on YouTube or Vimeo or TikTok in the browser.
Poster thumb, not a video tag
Email clients ignore <video>. Campaign creative is a hosted poster image wrapped in an outbound link. Toggle the play overlay to see what Klaviyo gets after push.
In React Email the same atom becomes an Img inside Link, plus a VML background fill for Outlook. Partial Sanity config (poster without href, or href without poster) omits the block entirely.
The reusable atom is not a media element. It is a hosted raster poster (optional play-button overlay) wrapped in an outbound Link. React Email renders Img plus Link for most clients. Outlook needs a VML shell around the background poster: <!--[if mso]><v:rect>...<v:fill src="..." />. Same href, same alt, same raster. Without the VML branch, Outlook shows a broken icon or nothing.
Sanity holds campaign video fields: poster asset, alt text, external href. At Klaviyo push time a mapper bakes props into the template. It returns null unless both poster URL and href exist. Poster without link? Omit the block. Link without poster? Also omit. Partial config should not ship a half-built thumb that clicks nowhere or shows alt text on a missing image.
Do not hand-edit CODE in Klaviyo after push. The next deploy overwrites it. Fix the React Email component or the Sanity fields, then push again.
export function EmailVideoThumb({ alt, href, src, showPlayOverlay = true }: Props) {
// Link wraps Img, or backgroundImage + play overlay
// Outlook: <!--[if mso]><v:rect>...<v:fill src=... />...
}
export function mapEmailSettingsToCampaignVideo(result): Props | null {
const posterSrc = mapSanityImageAssetUrl(result.campaignVideoPoster, ...)
const href = result.campaignVideoHref?.trim() ?? ''
if (!posterSrc || href.length === 0) return null
return { alt: result.campaignVideoAlt?.trim() ?? '', href, posterSrc }
}If you want video in email, ship a picture that lies honestly about being a link.
Happy coding! Sander