---
url: https://docs.ridgedata.ai/guide/state.md
---
# Dashboard state

Dashboard state is an object keyed by component id. Each component has named values, such as
`filter`. Iframe and JavaScript embeds use the same format:

```js
{
  "region-menu": { filter: "EMEA" },
  "date-range": { filter: ["2026-01-01", "2026-06-30"] }
}
```

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).

## Read state

::: code-group

```js [Iframe]
function getState() {
  return new Promise((resolve) => {
    const requestId = crypto.randomUUID();

    function onMessage(event) {
      if (event.source !== frame.contentWindow || event.origin !== ridgeOrigin) return;
      if (event.data?.type !== "ridge:state" || event.data.requestId !== requestId) return;
      window.removeEventListener("message", onMessage);
      resolve(event.data.state);
    }

    window.addEventListener("message", onMessage);
    frame.contentWindow.postMessage({ type: "ridge:getState", requestId }, ridgeOrigin);
  });
}

const state = await getState();
```

```js [JavaScript]
await ridge.ready;

const state = ridge.getState(); // every component
const region = ridge.getState("region-menu"); // one component, or null
```

:::

A JavaScript read before `ridge.ready` throws. For an iframe, wait for `ridge:ready` before
sending requests. Match replies by `requestId` because your app may have several pending
requests.

## Write state

Updates merge by component id and state key. Values you omit stay unchanged. Set a value to
`null` to clear it.

::: code-group

```js [Iframe]
frame.contentWindow.postMessage(
  {
    type: "ridge:setState",
    state: { "region-menu": { filter: "EMEA" } }
  },
  ridgeOrigin
);
```

```js [JavaScript]
const result = await ridge.setState({
  "region-menu": { filter: "EMEA" }
});

result.state; // complete state after rendering
result.changed; // component ids that changed
result.errors; // rejected component writes
```

:::

The iframe does not send a response to `ridge:setState`. Listen for `ridge:error` to detect
rejected updates. The JavaScript call returns the updated state and any errors after queries
and rendering finish. Some updates in a batch may succeed while others fail.

To clear the filter in either example, write `{ "region-menu": { filter: null } }`.

## Discover the state schema

The state schema tells you which component ids and keys exist, what values they accept, and
whether they are writable.

::: code-group

```js [Iframe]
const requestId = crypto.randomUUID();
frame.contentWindow.postMessage({ type: "ridge:getStateSchema", requestId }, ridgeOrigin);

// Listen for:
// { type: "ridge:stateSchema", requestId, stateSchema: { ... } }
```

```js [JavaScript]
await ridge.ready;
const stateSchema = ridge.getStateSchema();
```

:::

Each value's `schema` is a JSON Schema draft 2020-12 fragment. Use it to validate values from
your app's controls before sending them to the dashboard.

## Suppress write notifications

Use `silent` when your app updates dashboard state and does not need a change notification.

::: code-group

```js [Iframe]
frame.contentWindow.postMessage(
  { type: "ridge:setState", state: values, silent: true },
  ridgeOrigin
);
```

```js [JavaScript]
await ridge.setState(values, { silent: true });
```

:::

With `silent`, updates are still validated and applied, but no change notification is sent.

## Initial state

Apply initial state before the dashboard becomes visible to avoid a flash of its default
view.

::: code-group

```text [Iframe]
Set the initial state in Ridge's embed playground and copy the resulting signed iframe URL.
The iframe reads its `state` URL parameter once when it mounts.
```

```js [JavaScript]
import { RidgeEmbed } from "https://app.ridgedata.ai/api/embed/loader";

const ridge = await RidgeEmbed.load({
  jwt: token,
  dashboardId: "dashboard-id-123",
  rootElementId: "ridge-dashboard",
  state: { "region-menu": { filter: "EMEA" } }
});
```

:::
