// preview in
saved for every page

Image

Shows a photo in a frame, with an optional caption, a loading skeleton and a fallback that keeps the layout when the file fails to load.

Pattern HTML figure + imgClient componentSource ↗

Installation

Set up Camaleo once (Installation), then import it. It is a client component, because it watches the image's error event.

import { Image } from "@camaleo/react";

Anatomy

A <figure> while the image works, and a <div> in its place when it fails. style and className go to the outer element; every other prop goes to the <img>.

When it fails, the default fallback replaces the whole figure:

Examples

Every example is a real file you can copy. They all wear the skin, mode and brand from the bar above.

minimal · light · #3fa34d
Two rows of green mountain ridges under an orange evening sky, with the sun low on the right
Two rows of green mountain ridges under an orange evening sky, with the sun low on the right
Serra da Bocaina at duskPhoto: Ana Lima

Caption and credit

caption becomes a <figcaption> that screen readers tie to the image; put the credit or file size in captionMeta.

Two rows of green mountain ridges under an orange evening sky, with the sun low on the right
Two rows of green mountain ridges under an orange evening sky, with the sun low on the right

Size and crop

width and height reserve the space before the file arrives; an aspectRatio in style crops the same photo to 16:9 or 1:1 with object-fit: cover.

Two rows of green mountain ridges under an orange evening sky, with the sun low on the right

Loading skeleton

Set loadingState while you wait for the file and clear it in onLoad; the component doesn't clear it for you.

Couldn’t load the cover photo

serra-da-bocaina-cover.jpg

When the file fails

The default fallback keeps the frame and shows the file name. It takes role="img" and the alt as its name, so screen readers still hear what was meant to be there.

Photo didn’t load

The connection dropped. The file is still on the server.

Fallback with a retry

Click Try again. A custom fallback can hold controls, and it gets role="group" so they stay reachable; after the retry, focus stays on the box and a status message confirms it.

Two rows of green mountain ridges under an orange evening sky, with the sun low on the right

The three states

Switch between loaded, loading and failed. The failed state is read only on mount, so change key when you need to reset it.

Welcome to the Rio office

Rua da Assembleia 10, 18th floor. Reception opens at 8 am.

Decorative image

Use alt="" only when the text next to it already says everything. alt defaults to empty, so a photo without alt is hidden from screen readers too.

In context

Images usually sit at the top of a card or in a grid of thumbnails. These are whole pieces of UI you can copy, in the skin you picked.

Two rows of green mountain ridges under an orange evening sky, with the sun low on the right
Photo: Ana Lima
Travel

Three days hiking the Serra da Bocaina

Ana Lima · Oct 28, 2026 · 6 min read

Article cardCard · Image · Badge
Mechanical keyboard with dark keys and a green Enter key, seen from above

Mechanical keyboard K2

$149.00

Product cardCard · Image · Button

Media library

3 files · Campaign “Serra 2026”

Two rows of green mountain ridges under an orange evening sky, with the sun low on the right
ridges-dusk.jpg2.4 MB
Small sailboat on a calm blue sea at midday
sailboat.jpg1.1 MB

File not found

team-photo.jpg

Media libraryCard · Image

Guidelines

Two green mountain ridges under an orange evening sky
alt="Two green mountain ridges under an orange evening sky"
DoDescribe what the image shows.

Say what matters in this context, in one sentence, as you would to someone on the phone.

image.png
alt="image.png"
Don’tA file name as alt.

Screen readers already say "image", and "image.png" tells nothing about the content.

Two rows of green mountain ridges under an orange evening sky, with the sun low on the right
Small sailboat on a calm blue sea at midday
Two green mountain ridges under an orange evening sky
DoOne ratio per grid.

Give every thumbnail the same aspectRatio, so rows line up whatever shape each photo has.

Two rows of green mountain ridges under an orange evening sky, with the sun low on the right
Small sailboat on a calm blue sea at midday
Two green mountain ridges under an orange evening sky
Don’tEach image in its own ratio.

The row turns ragged, and the eye follows the gaps instead of the pictures.

Props

PropTypeDefaultDescription
fallbackReactNode—Replaces the default fallback when the image fails, e.g. with a retry button. It is wrapped in role="group" named by alt.
fallbackTitleReactNode"Failed to load image"Heading inside the default fallback.
fallbackSubtitleReactNode—Second line of the default fallback, such as the file name. Falls back to src.
captionReactNode—Text below the image, rendered as <figcaption>.
captionMetaReactNode—Secondary text at the end of the caption row, such as a credit or file size.
defaultErrorbooleanfalseStarts in the failed state. Read on mount only: change key to reset it.
loadingStatebooleanfalseCovers the image with a skeleton. You control it: clear it in onLoad.
onLoadReact.ReactEventHandler<HTMLImageElement>—Runs when the file has loaded. Clear loadingState here.
onErrorReact.ReactEventHandler<HTMLImageElement>—Runs when the file fails; the fallback then takes its place.
altstring—What the image shows. It also names the fallback. Defaults to "", which marks the image as decorative.
heightstring | number—Height of the file. With width, the browser reserves the space before it loads.
srcstring—The file to show. Without it, the fallback shows right away.
widthstring | number—Width of the file. With height, the browser reserves the space before it loads.

style and className go to the outer <figure> or fallback; an aspectRatio or height there crops the image. Every other <img> attribute, like loading="lazy" or srcSet, goes to the image.

Styling hooks

Restyle it without forking: target the slots, or set the tokens on a parent, a skin or the image itself.

Attributes

data-loadingon image, while loadingState is on

Tokens it reads

Color

--cml-border--cml-border-control--cml-card-border--cml-card-shadow--cml-fg--cml-fg-secondary--cml-surface--cml-surface-raised

Shape & size

--cml-border-width--cml-radius-popover

This component only

--cml-corner-shape

Accessibility

TabThe image is not a Tab stop. Tab reaches only controls inside a custom fallback, like Try again.
Was this page helpful? · Edit this page on GitHub