> ## Documentation Index
> Fetch the complete documentation index at: https://autorender.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Events and classes

> The CSS classes and DOM events Autorender Native puts on every element it manages — for styling load states and hooking your own JavaScript into the image lifecycle.

Autorender Native marks every element it manages with a lifecycle **CSS class** and fires a **DOM event** at each stage of loading. Use them to style placeholders, fade images in, measure load time, or run your own code when an image settles.

Both are optional. You never have to listen for an event or style a class — the runtime delivers images without either.

<Info>
  Everything here is client-side and read-only. Event details carry operational sizing information only — never signed URLs, API keys, or private metadata. A browser without `CustomEvent` still loads images; the events are skipped, never fatal.
</Info>

## What CSS classes does an element get?

The runtime keeps exactly **one** lifecycle class on each element at a time and swaps it as the state changes. Images and background elements use separate class names, so you can style the two independently.

| Stage               | Image (`data-ar-src`) | Background (`data-ar-background`) |
| ------------------- | --------------------- | --------------------------------- |
| Loading a variant   | `ar-loading`          | `ar-background-loading`           |
| Loaded successfully | `ar-done`             | `ar-background-done`              |
| Load failed         | `ar-error`            | `ar-background-error`             |

The class updates in place: an image carries `ar-loading` while its variant fetches, then that class is removed and `ar-done` — or `ar-error` — is added.

An upgrade re-enters `ar-loading`. When a box grows past a `step` boundary the runtime fetches a larger variant, so a settled image goes `ar-done` → `ar-loading` → `ar-done` again. Style the states with this in mind — see the placeholder pattern below.

The `ar-` prefix is configurable. Set [`class-prefix`](/docs/native/configuration) on the script URL to rename every class at once — `class-prefix=acme` turns `ar-done` into `acme-done` and `ar-background-done` into `acme-background-done` — when your site already uses `ar-` class names.

```css theme={null}
/* Placeholder tint while the first variant loads */
img.ar-loading { background-color: #1f2430; }
.ar-background-loading { background-color: #1f2430; }
```

The tint is safe across upgrades: on the first load the image has no pixels yet, so the tint shows; on an upgrade the current variant stays painted on top, so the tint never reappears.

<Warning>
  An `opacity` fade keyed on `ar-loading` or `ar-done` dips on every upgrade, because an upgrade removes `ar-done` and re-adds `ar-loading` while the larger variant fetches. Use a placeholder tint for load states, not an opacity fade, on any image that can grow past a `step` boundary.
</Warning>

## What events does the runtime emit?

Every event is a `CustomEvent` dispatched on the element, and every one **bubbles** — so you can listen on a container instead of wiring up each image.

| Event          | Fires when                                                            |
| -------------- | --------------------------------------------------------------------- |
| `ar:loadstart` | The runtime begins fetching a variant                                 |
| `ar:load`      | The first variant loads successfully                                  |
| `ar:upgrade`   | A larger variant replaces the current one after the box grew          |
| `ar:error`     | The variant fails to load                                             |
| `ar:resize`    | The element's box crosses a `step` boundary, so a new width is needed |

`ar:load` fires once, on the first successful load. Each later growth past a `step` boundary fires `ar:upgrade`, not another `ar:load` — Autorender Native only ever grows a variant, never shrinks it.

On `ar:error` the runtime does not retry. If the element had a real `src` before Autorender took over, that original source is restored so the visitor still sees an image.

A marked `<img>` with only `data-ar-src` and no real `src` has nothing to restore, so a failed variant leaves a broken image. That src-less form is the recommended setup everywhere except the [hero](/docs/native/hero), and there is no `data-ar-fallback` attribute — so for any image you cannot afford to lose, listen for `ar:error` and set your own fallback.

```js theme={null}
// Listen once on a gallery container — events bubble up from each image
document.querySelector('.gallery').addEventListener('ar:load', (event) => {
  console.log(event.target, 'loaded in', event.detail.durationMs, 'ms');
  // <img data-ar-src="gallery/one.jpg"> loaded in 84 ms
});

// No data-ar-fallback attribute exists — supply a fallback yourself on error
document.querySelector('.gallery').addEventListener('ar:error', (event) => {
  event.target.src = '/images/placeholder.png';
});
```

## In what order does it happen?

Each element moves through a fixed sequence. Only three states set a class; the waiting states are silent.

| Stage                                                | Class                 | Event          |
| ---------------------------------------------------- | --------------------- | -------------- |
| Registered, waiting for the viewport, then measuring | none                  | —              |
| Fetching the first variant                           | `ar-loading`          | `ar:loadstart` |
| First variant loaded                                 | `ar-done`             | `ar:load`      |
| Box grew past a `step` boundary                      | `ar-done` (unchanged) | `ar:resize`    |
| Fetching the larger variant                          | `ar-loading`          | `ar:loadstart` |
| Larger variant loaded                                | `ar-done`             | `ar:upgrade`   |
| A variant failed to load                             | `ar-error`            | `ar:error`     |

The last three rows repeat for every growth past a `step` boundary. On an upgrade, `ar:resize` always fires **before** the second `ar:loadstart`, and `ar:resize` fires only when a variant already loaded and the box size actually changed. `ar:error` is terminal — the runtime does not retry.

## What is in the event detail?

Every event carries a `detail` object describing the sizing decision behind the request:

| Field             | Type                      | Present on                          | Meaning                                                                                                                                     |
| ----------------- | ------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `sourceType`      | `'image' \| 'background'` | all events                          | Which loader handled the element                                                                                                            |
| `cssWidth`        | number                    | all events                          | Measured CSS width, in pixels                                                                                                               |
| `cssHeight`       | number                    | all events                          | Measured CSS height, in pixels                                                                                                              |
| `requestedWidth`  | number                    | all events                          | The bucketed width in the delivery URL (`w_…`)                                                                                              |
| `requestedHeight` | number                    | when a height was requested         | The bucketed height (`h_…`); omitted for width-only requests                                                                                |
| `dpr`             | number                    | all events                          | The clamped device pixel ratio folded into the request                                                                                      |
| `durationMs`      | number                    | `ar:load`, `ar:upgrade`, `ar:error` | Milliseconds from the most recent `ar:loadstart` to this event — on `ar:upgrade`, from the upgrade's own `ar:loadstart`, not the first load |

## Next steps

<CardGroup cols={2}>
  <Card title="Attributes" icon="code" iconType="solid" href="/docs/native/attributes">
    Every `data-ar-*` attribute that drives the elements these events fire on.
  </Card>

  <Card title="Configuration" icon="sliders" iconType="solid" href="/docs/native/configuration">
    The `step` boundary that triggers `ar:resize` and `ar:upgrade`.
  </Card>

  <Card title="Automatic optimization" icon="wand-magic-sparkles" iconType="solid" href="/docs/optimization/automatic">
    How the delivery layer picks the format, width, and quality behind each request.
  </Card>

  <Card title="Introduction" icon="bolt" iconType="solid" href="/docs/native/introduction">
    Add the script and load your first image.
  </Card>
</CardGroup>
