---
title: "Save and restore app URL state"
slug: "save-and-restore-app-url-state"
abstract: "How to save a Marketplace app's view in the host URL and restore it when the user reloads or shares the page."
---
# Save and restore app URL state

*Required extension points: any*

*Required SDK packages: client*

*Required API access: none*

Use `application.urlState` to [save a view in the host URL](https://doc.sitecore.com/mp/en/developers/sdk/0/sitecore-marketplace-sdk/understanding-app-url-state.html) and restore it after a reload or when opening a shared link.

> [!note]
> **Before you begin**
>
> - Complete an [SDK quick start](https://doc.sitecore.com/mp/en/developers/sdk/0/sitecore-marketplace-sdk/quick-starts.html) using `client` version `0.5.0` or later.
> - Review [how app URL state works](https://doc.sitecore.com/mp/en/developers/sdk/0/sitecore-marketplace-sdk/understanding-app-url-state.html).
> - Review the example `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).

## Restore a saved view

To restore a view in the component where you run `useMarketplaceClient`:

1. Update your imports and define the view type and a validator outside the component. Use a `type` alias, not an `interface`, to ensure the view is compatible with `JsonObject`:

   ```typescript
   import { useEffect, useRef, useState } from "react";
   import type { JsonObject } from "@sitecore-marketplace-sdk/client";

   type ViewState = { tab: "overview" | "seo"; range: "7d" | "30d" };
   const DEFAULT_VIEW: ViewState = { tab: "overview", range: "7d" };

   function parseViewState(raw: JsonObject | null | undefined): ViewState | null {
     if (!raw) return null;
     const { tab, range } = raw;
     if (tab !== "overview" && tab !== "seo") return null;
     if (range !== "7d" && range !== "30d") return null;
     return { tab, range };
   }
   ```

2. Inside the component, add state and an Effect Hook that queries the host after the SDK connects:

   ```typescript
   const [view, setView] = useState<ViewState>(DEFAULT_VIEW);
   const [isSaving, setIsSaving] = useState(false);
   const hasChanged = useRef(false);

   useEffect(() => {
     if (!client) return;
     let active = true;
     client.query("application.urlState")
       .then(({ data, error }) => {
         if (!active || hasChanged.current) return;
         if (error) {
           console.error("Error restoring app URL state:", error);
           return;
         }
         const savedView = parseViewState(data?.state);
         if (data?.state && !savedView) {
           console.warn("Ignoring invalid saved view.");
         }
         setView(savedView ?? DEFAULT_VIEW);
       })
       .catch((error) => console.error("Error querying app URL state:", error));
     return () => { active = false; };
   }, [client]);
   ```

   The validator checks the saved values before using them. The ref prevents a late response from overwriting a view the user has already changed.

## Save or clear a view

1. In the same component, add a function to save a complete view object or clear it with `null`:
    ```typescript
    const saveView = async (state: ViewState | null) => {
      if (!client) {
        console.error("The Marketplace client is not initialized.");
        return;
      }
      hasChanged.current = true;
      setView(state ?? DEFAULT_VIEW);
      setIsSaving(true);
      try {
        await client.mutate("application.urlState", { params: { state } });
      } catch (error) {
        console.error("Error saving app URL state:", error);
      } finally {
        setIsSaving(false);
      }
    };
    ```
2. Add controls to your existing return statement, then save your changes:
    ```tsx
    <>
      <p>Tab: {view.tab}. Date range: {view.range}.</p>
      <button disabled={!client || isSaving} onClick={() => saveView({ tab: "seo", range: "30d" })}>Show SEO</button>
      <button disabled={!client || isSaving} onClick={() => saveView(null)}>Reset view</button>
    </>
    ```
    This adds a **Show SEO** button and a **Reset view** button to your app's UI.
    If you add other controls, set `hasChanged.current = true` when the user changes the view.

## Test saving and restoring a view

1. Open your Marketplace app in one of its extension points and click **Show SEO**. Wait for the host URL to update, then reload the page. The view shows **seo** and **30d**. This shows that the app URL state is successfully restored.
2. Click **Reset view**, wait for the URL update, then reload. The view shows **overview** and **7d**. This shows that the app URL state is successfully cleared.
