---
title: "Understanding app URL state"
slug: "understanding-app-url-state"
abstract: "Understand how to save a Marketplace app's view state in the host URL so users can restore or share the view."
---
# Understanding app URL state

As users navigate your Marketplace app, they might select tabs, filters, and other items in the app UI. For example, they might select your app's **Optimizations** tab, open the **SEO dashboard**, and select **Last 30 days** in the date picker.

Without app URL state, users need to repeat these selections when they reload the page or open your app again. App URL state lets you save the selections in the URL of the Sitecore page that hosts your app. Your app can then restore the view when a user reloads the page, reopens the app in the same host session, or opens a link shared by another user.

Your app supplies a JSON object, and the host encodes it in the `mkp_url_state` query parameter of the URL.

To set up app URL state, see [Save and restore app URL state](https://doc.sitecore.com/mp/en/developers/sdk/0/sitecore-marketplace-sdk/save-and-restore-app-url-state.html).

> [!important]
> App URL state requires `@sitecore-marketplace-sdk/client` version `0.5.0` or later.

## Implementing app URL state

Using the `application.urlState` [query](https://doc.sitecore.com/mp/en/developers/sdk/0/sitecore-marketplace-sdk/understanding-queries.html#app-url-state) and [mutation](https://doc.sitecore.com/mp/en/developers/sdk/0/sitecore-marketplace-sdk/understanding-mutations.html#app-url-state), you can take three actions with the app URL state: read, save, or clear it.

Consider a Marketplace app that has multiple tabs and a date picker. The following example shows how to save the **Optimizations** tab and the **30 days** range in the date picker into the app URL state:

```typescript
// Save the view
try {
  await client.mutate('application.urlState', {
    params: { state: { tab: 'optimizations', range: '30d' } },
  });
} catch (error) {
  console.error('Error saving app URL state:', error);
}
```

The fields `tab` and `range` are properties of the saved state object. The host handles the encoding and URL update.

To read the state:

```typescript
const { data, error } = await client.query('application.urlState');
if (error) {
  console.error('Error retrieving app URL state:', error);
} else {
  const savedState = data?.state ?? null;
  // Validate savedState before restoring the view.
}
```

To reset the state:

```typescript
// Clear the saved view
try {
  await client.mutate('application.urlState', {
    params: { state: null },
  });
} catch (error) {
  console.error('Error clearing app URL state:', error);
}
```

Note the following about state:

- Saving state replaces the previous state. Saving `null` clears the state.
- The SDK validates the state before sending it. If validation fails, the mutation rejects and the previously saved state remains unchanged.
- The current state is stored in memory. A query after a successful save returns the new value, even if the URL hasn't updated yet.
- URL updates can briefly lag behind saves, so you need to wait for the browser URL to update before copying it.

## Scope and lifetime

State belongs to an app instance within a specific Sitecore organization and environment.

Each app instance accesses its own state through the SDK, including instances of the same app. A link opened in another organization or environment doesn't restore its state, and switching environments resets it.

Closing and reopening an app in the same session keeps its state unless the host removes it to meet the shared [size limit](https://doc.sitecore.com/mp/en/developers/sdk/0/sitecore-marketplace-sdk/understanding-app-url-state.html#supported-values-and-size-limits).

View state is separate from route persistence, and existing route events continue to work as before.

## Supported values and size limits

Save state as a plain JSON object. The object can contain strings, finite numbers, booleans, `null`, arrays, and nested objects. The top-level value must be an object or `null`.

The object can't contain functions, `Date`, `Map`, `Set`, class instances, circular references, `NaN`, or `Infinity`.

Object properties set to `undefined` are omitted, including nested properties. `undefined` values in arrays are rejected.


| Limit                                            | Maximum                                |
| ------------------------------------------------ | -------------------------------------- |
| Encoded state per app instance                   | 1,024 characters, including the prefix |
| Uncompressed JSON per app instance               | 8,192 UTF-8 bytes                      |
| Nesting depth                                    | 32 levels                              |
| Encoded state for all instances on the host page | 4,096 characters                       |

Both encoded and uncompressed limits apply. The nesting limit includes the top-level object. It's best practice to save IDs rather than complete content items as state. When the shared URL budget is full, the host can remove the saved states of other instances. Always handle `state: null`, even after an earlier save.

## Security

URL state isn't encrypted. Anyone with the URL can read or change it, and URLs can appear in browser history and logs.

It's best practice to:
- Not save secrets or personal data in the state.
- Not log the state.
- Validate restored values and check the user's permissions before loading referenced data. A saved view doesn't grant access to the data.

## Handle errors

The `client` package exports `ErrorCode` and `UrlStateErrorReason` for checking error values.

| Code | Reason | Action |
| --- | --- | --- |
| `METHOD_NOT_FOUND` | None | The feature isn't available in this host. Keep the view in memory. |
| `INVALID_REQUEST` | `NOT_PLAIN_OBJECT` | Save a plain object or `null`. |
| `INVALID_REQUEST` | `NOT_SERIALIZABLE` | Remove unsupported values, `undefined` values in arrays, or unsafe property names, and check the nesting depth. |
| `INVALID_REQUEST` | `STATE_TOO_LARGE` | Save less data, such as IDs instead of complete items. |
| `INVALID_STATE` | None | Reopen the app in the intended organization and environment. |
| `TIMEOUT` | None | Query the state before retrying; the save might have succeeded. |
