Image
An img the host app can replace with its own image component, one at a time or everywhere at once.
Quick Preview
One image, swapped with render
Every image below an ImageProvider
Usage
Image is an <img> and nothing more — no sizing, no object-fit, no loading behaviour of its own. What it adds is a seam: render swaps the element for another component, and ImageProvider does the same for a whole tree.
import { Image, ImageProvider } from "@giddaa-housing/ui/image";
import NextImage from "next/image";
// One image.
<Image
render={(props) => <NextImage {...props} fill sizes="100vw" />}
src={listing.heroUrl}
alt={listing.heroAlt}
/>;
// Or name it once, at the root, for every image in the library.
<ImageProvider render={(props) => <NextImage {...props} fill sizes="100vw" />}>
<App />
</ImageProvider>;render also takes an element to clone, which is shorter when there are no extra props to add:
<Image render={<NextImage fill />} src={src} alt={alt} />Every <img> prop passes straight through — loading, fetchPriority, sizes, srcSet, decoding, referrerPolicy. Deciding that a photo is below the fold is the call site's job, not the library's, so nothing here is set for you:
<PropertyListingCardImage src={image.src} alt={image.alt} loading="lazy" />Which components read the provider
PropertyListingCard, PhotoGallery, MediaGalleryHero and VideoPlayerDialog's playlist posters — the components that render photography from a URL the app supplies.
FileUpload's previews and MediaPlayer's seek-preview sprites deliberately stay on a plain <img>. Their sources are made in the browser: a blob: URL from URL.createObjectURL, or a sprite sheet positioned by transform. An optimising loader has nothing to fetch from the first and would break the second.
Examples
Custom image component
The framed images below are rendered by a stand-in component rather than the built-in <img>. The first pair swaps a single image through render; the gallery underneath swaps every image at once through ImageProvider.
One image, swapped with render
Every image below an ImageProvider
Props
| Prop/API | Type | Default | Description |
|---|---|---|---|
render | ReactElement | (props, state) => ReactElement | - | Replaces the <img>. A function receives the merged img props, including the resolved className. |
slot | string | "image" | Written to data-slot, for styling and for finding the image in tests. |
| native img props | React img props | - | Forwarded unchanged, loading and fetchPriority included. |
ImageProvider | { render?, children } | - | Supplies the default render to every image below it. An image's own render wins. |
useImageRender | (render?) => ImageRender | - | Resolves an image's own render against the provider's, for components building their own image slot. |
Accessibility
altis not defaulted. Pass a description when the photo carries information, andalt=""when it is decorative and the surrounding text already says what it shows.- A replacement component still has to render an
<img>eventually, or supply an equivalent accessible name —rendermergesaltinto the props it hands over, but only that component can put it on an element. - Custom components that wrap their image in extra elements can break a parent's
[&>img]styling; check the layout after a swap.