# Image

> A responsive, lazy-loaded image — wraps astro:assets Image with sensible defaults.

URL: https://apogee.avaui.dev/docs/image
Last updated: 2026-10-15

A responsive, lazy-loaded image — wraps astro:assets Image with sensible defaults.

Last updated October 15, 2026

Uses JavaScript

Only when a `fallback` slot is present: a small island listens for the image’s `error` event and swaps the image for the fallback in the same box, carrying the alt text over as an accessible name.

**Without JS:** The broken image shows its alt text; the fallback stays hidden.

```astro
<Image src={placeholder} alt="Placeholder" />
```

## Add

```sh
npx @ava-ui/apogee add image
```

No Apogee dependencies. Wraps Astro’s built-in `astro:assets` `Image` — not an added dependency.

## Usage

```astro
---
import Image from '@/components/apogee/Image/Image.astro'
import hero from '../assets/hero.jpg'
---
<Image src={hero} alt="Team meeting around a table" />
```

## API

`@composable` — exposed to Content Editors in AVA. Any other `astro:assets` `Image` prop — `width`, `height`, `widths`, `sizes`, `format`, `quality`, `densities`, … — passes through.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `src` | `ImageMetadata \| string` | — | Required. A local image import (optimised at build) or a remote URL (sized with `inferSize` unless `width`/`height` are given). |
| `alt` | `string` | — | Required. Alt text (`""` for decorative images). |
| `loading` | `'lazy' \| 'eager'` | `'lazy'` | Native lazy-loading. Use `eager` for the page’s LCP image. |
| `decoding` | `'async' \| 'sync' \| 'auto'` | `'async'` | Native image decoding hint. |
| `width` / `height` | `number` | — | Intrinsic size; sets the aspect ratio and the largest generated width. |
| `widths` | `number[]` | — | Responsive candidates for `srcset`, e.g. `[480, 800, 1200]`. |
| `sizes` | `string` | — | The `sizes` attribute that picks a candidate; pair it with `widths`. |
| `quality` | `'low' \| 'mid' \| 'high' \| 'max' \| number` | — | Compression of optimised local images. |
| `format` | `'avif' \| 'webp' \| 'png' \| 'jpeg' \| 'svg'` | — | Output format of optimised local images (Astro defaults to `webp`). |
| `fit` | `'cover' \| 'contain'` | — | How the image fills a box sized by `width`/`height` or a class. |
| `radius` | `'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'` | — | Corner radius token. |
| `outline` | `boolean` | `false` | Inset hairline for images that blend into the page. |
| `class` | `string` | — | Extra classes, merged last (`max-w-full` always, plus `h-auto` unless `fit` is set). |

Slot `fallback`: shown in the image’s box when it fails to load (a small island swaps them; without JS the broken image shows its alt text).

## Fit and radius

`fit` decides what happens when the box and the image disagree: `cover` crops, `contain` letterboxes. Both boxes below are square; the image is 16:9.

```astro
<Image src={placeholder} alt="Cover" width={320} height={180} fit="cover" class="size-40" />
<Image src={placeholder} alt="Contain" width={320} height={180} fit="contain" class="size-40 bg-neutral-faded" />
```

Every `radius`:

```astro
<Image src={placeholder} alt="Radius sm" width={96} height={96} fit="cover" radius="sm" class="size-24" />
<Image src={placeholder} alt="Radius md" width={96} height={96} fit="cover" radius="md" class="size-24" />
<Image src={placeholder} alt="Radius lg" width={96} height={96} fit="cover" radius="lg" class="size-24" />
<Image src={placeholder} alt="Radius xl" width={96} height={96} fit="cover" radius="xl" class="size-24" />
<Image src={placeholder} alt="Radius full" width={96} height={96} fit="cover" radius="full" class="size-24" />
```

## States

-   **Loading** — `loading="lazy"` by default; set `eager` on the page’s main image.
-   **Broken** — with a `fallback` slot, a failed load swaps in the fallback in the same box. Without JS, or without the slot, the browser shows the alt text.

## Examples

Eager-loaded hero image, so it isn’t deferred:

```astro
<Image src={hero} alt="Product screenshot" loading="eager" />
```

Responsive sizing:

```astro
<Image
  src={hero}
  alt="Product screenshot"
  widths={[400, 800, 1200]}
  sizes="(min-width: 768px) 50vw, 100vw"
/>
```

Format + quality override:

```astro
<Image src={hero} alt="Product screenshot" format="avif" quality={80} />
```

Remote source, fit and radius, outline, and a fallback for a broken URL:

```astro
<Image src="https://picsum.photos/seed/apogee/320/200" alt="A landscape" width={320} height={200} radius="lg" />
<Image src="https://picsum.photos/seed/apogee/320/200" alt="A landscape" width={160} height={160} fit="cover" radius="full" outline />
<Image src="https://example.invalid/missing.jpg" alt="Missing image" width={160} height={100} radius="md">
<div slot="fallback" class="grid place-items-center rounded-md bg-neutral-faded text-sm text-neutral-faded">No image</div>
</Image>
```

## Accessibility

-   `alt` is required — describe the image’s content or purpose; pass `alt=""` only for purely decorative images.
-   `loading="lazy"` (the default) defers offscreen images without JS. Set `loading="eager"` on the page’s LCP image so it isn’t deferred.
-   No JS — `astro:assets` processes the image ahead of time; output is a plain `<img>`.

## Tokens

None. `Image` has no themeable colour/spacing tokens of its own — `h-auto max-w-full` keeps it responsive by default; override sizing via `class`, `width`, or `height`.

## Do / Don’t

-   **Do** always import a local image (`ImageMetadata`) so `astro:assets` can optimize it — don’t pass a raw URL string.
-   **Do** set `loading="eager"` on the one image that’s the page’s LCP; leave everything else `lazy`.
-   **Don’t** upscale a small source image via `width`/`height` — `astro:assets` will warn or error.
-   **Don’t** skip `alt` — pass `alt=""` explicitly for decorative images rather than omitting it.

## Content

-   Alt text describes what’s in the image or its purpose (not “image of…”), kept short, no ending period unless it’s a full sentence.
-   Decorative images get `alt=""`, never omitted.

## Related

[

AspectRatioLocks its content to a fixed width-to-height ratio — media, embeds, maps.

](https://apogee.avaui.dev/docs/aspect-ratio)[

AvatarA user image, with an initials fallback derived from name.

](https://apogee.avaui.dev/docs/avatar)[

IconA library-agnostic icon wrapper — forwards a Lucide icon's props, or accepts a raw SVG slot, with built-in accessibility.

](https://apogee.avaui.dev/docs/icon)
