Image Composition
An <img> that reserves its space and fades in. It is deliberately not a Next.js Image: no
resizing service, no loader, no framework coupling — that belongs to whichever framework the app
already chose.
Usage
with a blur placeholder
flat background, no blur

broken source — the placeholder stays
<Image src={photo.url} alt="Abstract gradient" aspectRatio={4 / 3} blurDataUrl={photo.blur} radius="lg"/>What it handles
- The space is reserved through
aspectRatio, so nothing below the image jumps when it lands. - A cached image still fades in correctly. An image can finish loading before React attaches
onLoad, which is the bug that leaves a permanently blank frame; the ref checkscomplete. - A broken source keeps the placeholder rather than collapsing to a broken-image icon.
altis required, and""is allowed — but decorative has to be said out loud rather than achieved by forgetting the prop.
Do
Generate blurDataUrl at build time, a few hundred bytes at most; it ships inline with the markup.
Don't
Use a full-size image as its own placeholder — the point is the bytes that arrive first.
Props
| Prop | Type | Default | Notes |
|---|---|---|---|
| src / alt | string | — | alt is required; pass "" for decorative images. |
| aspectRatio | number | — | Width / height, e.g. 16 / 9. Reserves the box. |
| blurDataUrl | string | — | Tiny data URI, blurred and scaled up until the real image loads. |
| background | string | — | Flat colour behind the image when there is no blur placeholder. |
| fit | 'cover' | 'contain' | 'cover' | object-fit. |
| radius | 'none' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Corner token. |
| loading | 'lazy' | 'eager' | 'lazy' | Eager for anything above the fold. |