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

A modern home exteriorA bright living roomcustom

Every image below an ImageProvider

A modern home exteriorcustom
A bright living roomcustom
1 / 2

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

A modern home exteriorA bright living roomcustom

Every image below an ImageProvider

A modern home exteriorcustom
A bright living roomcustom
1 / 2

Props

Prop/APITypeDefaultDescription
renderReactElement | (props, state) => ReactElement-Replaces the <img>. A function receives the merged img props, including the resolved className.
slotstring"image"Written to data-slot, for styling and for finding the image in tests.
native img propsReact 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

  • alt is not defaulted. Pass a description when the photo carries information, and alt="" 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 — render merges alt into 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.

On this page