---
url: https://docs.ridgedata.ai/guide/events.md
---
# Events

Both embed types report when they are ready, when state changes, and when an update is
rejected. Iframes send window messages. JavaScript embeds let you register event listeners
on the dashboard handle.

The JavaScript API also reports filter changes and identifies whether a change came from
the user or your app. It provides methods to wait for rendering and export images.

In the iframe examples, replies and events are addressed to the origin of your first command
after `ridge:ready`; see [Host origin](./iframe-authentication#host-origin).

## Ready

::: code-group

```js [Iframe]
window.addEventListener("message", (event) => {
  if (event.source !== frame.contentWindow || event.origin !== ridgeOrigin) return;
  if (event.data?.type === "ridge:ready") {
    console.log("Dashboard ready", event.data.stateSchema);
  }
});
```

```js [JavaScript]
ridge.on("statechange", ({ state }) => console.log(state)); // safe before ready
await ridge.ready;
console.log("Dashboard ready", ridge.getStateSchema());
```

:::

Register an iframe message listener before loading the frame so its one-time `ridge:ready`
message cannot be missed. For JavaScript embeds, attach listeners as soon as the loader returns, before
awaiting `ready`.

## State changes

::: code-group

```js [Iframe]
window.addEventListener("message", (event) => {
  if (event.source !== frame.contentWindow || event.origin !== ridgeOrigin) return;
  if (event.data?.type === "ridge:stateChange") {
    syncMyControls(event.data.state);
  }
});
```

```js [JavaScript]
const unsubscribe = ridge.on("statechange", ({ state, changes, source }) => {
  if (source === "user") syncMyControls(state, changes);
});

// Later:
unsubscribe();
```

:::

The iframe reports the complete resulting state. The JavaScript event also reports which values changed,
and whether the change came from the user or API.

## Filter changes

::: code-group

```js [Iframe]
// Filters arrive inside ridge:stateChange like any other state key.
const region = event.data.state["region-menu"]?.filter;
```

```js [JavaScript]
ridge.on("filterchange", ({ filters, changes, source }) => {
  if (source === "user") syncFilterControls(filters, changes);
});
```

:::

Use `{ silent: true }` to update state without sending a change notification to your app.

## Rejected writes

::: code-group

```js [Iframe]
if (event.data?.type === "ridge:error") {
  const { id, code, message } = event.data.error;
  console.warn(code, id, message);
}
```

```js [JavaScript]
ridge.on("stateerror", ({ id, code, message }) => {
  console.warn(code, id, message);
});
```

:::

Use `code` to identify the error in your handler. Some updates in a batch may succeed even
when another component rejects its value.

## Export an image

Use the JavaScript API to export the dashboard as an image. It is not available through iframe
messages:

::: code-group

```text [Iframe]
Not supported by the iframe message protocol.
```

```js [JavaScript]
const png = await ridge.exportImage("png");
const blob = await ridge.exportImage("blob");
```

:::

`exportImage` waits for the dashboard's queries and rendering to finish, so the image includes the
latest state.

## Image export results

| `exportImage(type)` | Result                                                                                              |
| ------------------- | --------------------------------------------------------------------------------------------------- |
| `"png"` (default)   | PNG data URL string, usable as an image's `src`.                                                    |
| `"jpeg"`            | JPEG data URL string.                                                                               |
| `"svg"`             | SVG data URL string.                                                                                |
| `"blob"`            | PNG `Blob`. Use `URL.createObjectURL(blob)` for an image URL, then revoke it when no longer needed. |
| `"pixelData"`       | `Uint8ClampedArray` of RGBA pixel values.                                                           |

The result can be `null` if no dashboard image is available. Export failures can reject the
promise; handle them with `try`/`catch`.

## JavaScript event names

| Event          | Payload type        | Contents                                                                           |
| -------------- | ------------------- | ---------------------------------------------------------------------------------- |
| `statechange`  | `StateChangeEvent`  | Complete `state`, changed values in `changes`, and `source` (`"user"` or `"api"`). |
| `filterchange` | `FilterChangeEvent` | Active `filters`, changed filters in `changes`, and `source`.                      |
| `stateerror`   | `StateError`        | Component `id`, error `code`, and `message` for a rejected update.                 |

`ridge.on(name, listener)` returns a function that removes the listener. The
`RidgeEmbedEventMap` reference maps each event name to its payload type.
