Tasteful stand-ins for missing images: a token-tinted pattern (grid, dots, lines, rings or glow) picked deterministically from a seed string, with an optional glyph and caption. No assets, no network, no dependencies — it follows theme and accent. Image uses it automatically when there is no src, so blocks never need hard-coded stock photos.
Installation
The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";
Usage
import { Image, PlaceholderArt } from "@vamos-ui/react";
// Image with no src renders art, named by alt
<Image src={post.cover} alt={post.title} aspect="16/9" />
// Art while loading and on error
<Image src={url} alt="Team offsite" aspect="4/3" placeholder="art" />
// Standalone (fills its parent)
<PlaceholderArt seed="harbour-terrace" label="Harbour terrace" caption />Colours are color-mix() of --chart-1…6 and --surface-2, so the art follows dark mode and the accent (chart-1 is the accent ink).
PlaceholderArt has no client code and renders in server components. It fills its parent: give the parent a size, or use it inside AspectRatio or Image.
Placeholder art is a stand-in, not decoration: give it a label when the image carries meaning, so screen readers still hear what belongs there.
Examples
Patterns
The seed picks the pattern, tint (a chart palette slot), angle and focal point. pattern forces one.
Image without a src (blog list)
src null, undefined or "" renders art at the given aspect (4:3 if none) with alt as its accessible name. seed defaults to alt.
How we cut onboarding from 9 days to 3
Mira CastellA field guide to quarterly pricing reviews
Jonas FerreiraNotes from the spring partner summit
Ada LindqvistWhile loading and on error
placeholder="art" shows the art under a loading image and keeps it when the source fails. placeholderCaption prints the alt.

Site hero before photography
strength="bold" for large areas, a custom glyph and a caption that says what goes there.
Ceramics evenings at Fernwood Studio
Six Thursdays, small groups, clay and glazes included.
API
PlaceholderArt
| Prop | Type | Default |
|---|---|---|
seedAny stable string (slug, title, record id). Same seed → same art on server and client. | string | label |
patternForce a pattern; auto picks from the seed. | "auto" | "grid" | "dots" | "lines" | "rings" | "glow" | "auto" |
labelWhat the missing image would show. Set → role="img" with this name; unset → decorative (aria-hidden). | string | — |
iconCentre glyph in a small frosted ring. false hides it. | ReactNode | false | image glyph |
captiontrue prints the label in the bottom corner; or pass your own text. | boolean | ReactNode | — |
strengthbold raises the tint for large hero areas. | "subtle" | "bold" | "subtle" |
Image (placeholder props)
| Prop | Type | Default |
|---|---|---|
srcnull, undefined or "" renders art (alt required, "" = decorative). | string | MediaAsset | null | — |
placeholderart: seeded art while loading and as the error fallback (unless fallback is set). | "blur" | "color" | "skeleton" | "art" | "none" | — |
seedSeed for the art. | string | alt, then URL |
placeholderPatternForce the art pattern. | PlaceholderArtPattern | "auto" | "auto" |
placeholderIconGlyph in the art; false hides it. | ReactNode | false | — |
placeholderCaptiontrue prints alt as a caption on the art. | boolean | ReactNode | — |
Helpers (pure, server-safe)
| Prop | Type | Default |
|---|---|---|
placeholderArtRecipe(seed, pattern?){ pattern, hue, hue2, angle, cell, x, y } from an FNV-1a hash of the seed. | → PlaceholderArtRecipe | — |
placeholderArtStyle(recipe, strength?)The background for a recipe — use it on your own element. | → CSSProperties | — |
hashSeed(seed)32-bit FNV-1a hash. | → number | — |
PLACEHOLDER_ART_PATTERNSAll pattern names. | PlaceholderArtPattern[] | — |