---
title: State Persistence
product: Dashboard
description: Learn how to modify the default layout and other settings of a survey data dashboard and save them to the browser's localStorage to ensure more personalized experience. View free demo for JavaScript to learn more.
framework: React
source: https://surveyjs.io/dashboard/examples/save-dashboard-state-to-local-storage/reactjs
index: https://surveyjs.io/dashboard/examples/overview.md
---

# State Persistence (React)

A state in SurveyJS Dashboard is an object containing visualizer settings that a user has changed while working with the dashboard. These settings include the selected locale, chart types, chart layout, sorting, filtering, and others. You can save the state and restore it after users reload the page, allowing them to resume working with the dashboard from where they left off. In this demo, the state is saved to and restored from `localStorage`. To test the state persistence functionality, change chart types, select different sort orders, or change the layout using drag and drop, and then reload the page. You should see your customizations persisted.

To save the state, handle the [`onStateChanged`](https://surveyjs.io/dashboard/documentation/api-reference/dashboard#onStateChanged) event. A handling function contains the state object as the second parameter. Serialize this object to a JSON string and save this string to a desired storage.

To restore the state after a page reload, obtain the state as a JSON string from your storage, deserialize this string to a JSON object, and assign this object to the visualizer's [`state`](https://surveyjs.io/dashboard/documentation/api-reference/dashboard#state) property.

## Files

### `public/index.html`

```html
<div id="loadingIndicator" class="data-loading-indicator-panel">
  <div class="data-loading-indicator">
    <svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
      <g clip-path="url(#clip0_17928_11482)">
        <path d="M32 64C14.36 64 0 49.65 0 32C0 14.35 14.36 0 32 0C49.64 0 64 14.35 64 32C64 49.65 49.64 64 32 64ZM32 4C16.56 4 4 16.56 4 32C4 47.44 16.56 60 32 60C47.44 60 60 47.44 60 32C60 16.56 47.44 4 32 4Z" fill="#E5E5E5" />
        <path d="M53.2101 55.2104C52.7001 55.2104 52.1901 55.0104 51.8001 54.6204C51.0201 53.8404 51.0201 52.5704 51.8001 51.7904C57.0901 46.5004 60.0001 39.4704 60.0001 31.9904C60.0001 24.5104 57.0901 17.4804 51.8001 12.1904C51.0201 11.4104 51.0201 10.1404 51.8001 9.36039C52.5801 8.58039 53.8501 8.58039 54.6301 9.36039C60.6701 15.4004 64.0001 23.4404 64.0001 31.9904C64.0001 40.5404 60.6701 48.5704 54.6301 54.6204C54.2401 55.0104 53.7301 55.2104 53.2201 55.2104H53.2101Z" fill="#19B394" />
      </g>
      <defs>
        <clipPath id="clip0_17928_11482">
          <rect width="64" height="64" fill="white" />
        </clipPath>
      </defs>
    </svg>
  </div>
</div>
<div id="surveyDashboardComponent"></div>
```

### `src/surveydata.js`

```js
function randomIntFromInterval(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
}
function generateData() {
    const data = [];
    for (let index = 0; index < 100; index++) {
        data.push({
            nps: randomIntFromInterval(0, 10),
            "did-recommend": randomIntFromInterval(0, 1),
            "product-discovery": randomIntFromInterval(1, 7),
            "uses-product": randomIntFromInterval(0, 1),
            "used-libraries": [ randomIntFromInterval(1, 2), randomIntFromInterval(3, 4) ],
            "js-frameworks": randomIntFromInterval(1, 14),
            "backend-languages": randomIntFromInterval(1, 8),
            "devices-to-support": [ 1, randomIntFromInterval(2, 3) ]
        });
    }
    return data;
}
export const dataFromServer = generateData();
```

### `src/SurveyDashboardComponent.jsx`

```js
import React from "react";
import { Model } from "survey-core";
import { Dashboard } from "survey-analytics";
import "survey-analytics/survey.analytics.css";
import { DocumentHelper } from "survey-analytics";
import "./index.css";
import { json } from "./json";
import "survey-core/survey-core.min.css";
import { dataFromServer } from "./surveydata";

const savedState = localStorage.getItem("surveyJsDashboardState");
class SurveyDashboardComponent extends React.Component {
    componentDidMount() {
        const survey = new Model(json);
        // Imitate an asynchronous call that loads data from a server
        setTimeout(() => {
            const dashboard = new Dashboard({
                questions: survey.getAllQuestions(),
                data: dataFromServer,
                
            });
            
            
            
            if (!!savedState) {
                dashboard.state = JSON.parse(savedState);
            }
        
            dashboard.onStateChanged.add((_, state) => {
                localStorage.setItem("surveyJsDashboardState", JSON.stringify(state));
            });
        
            dashboard.registerToolbarItem("reload", () => {
                return DocumentHelper.createButton(
                    () => { location.reload(); },
                    "Reload the page"
                );
            });
        
            dashboard.registerToolbarItem("resetState", () => {
                return DocumentHelper.createButton(
                    () => {
                        localStorage.setItem("surveyJsDashboardState", "");
                        location.reload();
                    },
                    "Reset the state"
                );
            });
            
            document.getElementById("loadingIndicator").style.display = "none";
            dashboard.render("surveyDashboardContainer");
            
        }, 1000);
    }
    render() {
        return React.createElement("div", { id: "surveyDashboardContainer" });
    }
}

export default SurveyDashboardComponent;
```

### `src/index.css`

```css
/* You can add your custom CSS here. */
.data-loading-indicator-panel {
    width: 100%;
    height: 400px;
}
.data-loading-indicator {
    position: relative;
    width: 64px;
    height: 64px;
    left: calc((100% - 64px)/ 2);
    top: calc((100% - 64px)/ 2);
    animation: data-loading-indicator-spinner 1s infinite linear;
}
@keyframes data-loading-indicator-spinner {
    from {
        transform: rotate(0deg);
    }

    to {
        transform: rotate(359deg);
    }
}
```

### `src/index.js`

```js
import React from "react";
import { createRoot } from "react-dom/client";
import SurveyDashboardComponent from "./SurveyDashboardComponent";

const root = createRoot(document.getElementById("surveyDashboardComponent"));
root.render(<SurveyDashboardComponent />);
```

### `src/json.js`

```js
export const json = {
  "pages": [
    {
      "name": "promotion",
      "elements": [
        {
          "type": "rating",
          "name": "nps",
          "title": "How likely are you to recommend our product to a friend or colleague?",
          "isRequired": true,
          "rateMin": 0,
          "rateMax": 10,
          "minRateDescription": "Most unlikely",
          "maxRateDescription": "Most likely"
        },
        {
          "type": "radiogroup",
          "name": "did-recommend",
          "title": "Have you recommended our product to anyone?",
          "choices": [
            { "value": 1, "text": "Yes" },
            { "value": 0, "text": "No" }
          ]
        }
      ]
    },
    {
      "name": "customer-segregation",
      "elements": [
        {
          "type": "radiogroup",
          "name": "product-discovery",
          "title": "How did you first discover our product?",
          "choices": [
            { "value": 1, "text": "Search engine" },
            { "value": 2, "text": "GitHub" },
            { "value": 3, "text": "Friend or colleague" },
            { "value": 4, "text": "Reddit" },
            { "value": 5, "text": "Medium" },
            { "value": 6, "text": "Twitter" },
            { "value": 7, "text": "Facebook" }
          ]
        },
        {
          "type": "radiogroup",
          "name": "uses-product",
          "title": "Do you currently use our libraries?",
          "isRequired": true,
          "choices": [
            { "value": 1, "text": "Yes" },
            { "value": 0, "text": "No" }
          ]
        },
        {
          "type": "checkbox",
          "name": "used-libraries",
          "visibleIf": "{uses-product} = 1",
          "title": "Which libraries do you use?",
          "isRequired": true,
          "choices": [
            { "value": 1, "text": "Form Library" },
            { "value": 2, "text": "Survey Creator" },
            { "value": 3, "text": "Dashboard" },
            { "value": 4, "text": "PDF Generator" }
          ]
        }
      ]
    },
    {
      "name": "frameworks-and-devices",
      "elements": [
        {
          "type": "checkbox",
          "name": "js-frameworks",
          "title": "Which JavaScript frameworks do you use?",
          "choices": [
            { "value": 1, "text": "React" },
            { "value": 2, "text": "Angular" },
            { "value": 3, "text": "jQuery" },
            { "value": 4, "text": "Vue.js" },
            { "value": 5, "text": "Meteor" },
            { "value": 6, "text": "Ember" },
            { "value": 7, "text": "Backbone" },
            { "value": 8, "text": "Knockout" },
            { "value": 9, "text": "Aurelia" },
            { "value": 10, "text": "Polymer" },
            { "value": 11, "text": "Mithril" },
            { "value": 12, "text": "Svelte" },
            { "value": 13, "text": "Remix" },
            { "value": 14, "text": "Next.js" }
          ],
          "choicesOrder": "asc",
          "colCount": 3
        },
        {
          "type": "checkbox",
          "name": "backend-languages",
          "title": "Which backend programming languages do you use?",
          "choices": [
            { "value": 1, "text": "Java" },
            { "value": 2, "text": "Python" },
            { "value": 3, "text": "JavaScript / Node.js" },
            { "value": 4, "text": "Go" },
            { "value": 5, "text": "Django" },
            { "value": 6, "text": "C# / ASP.NET" },
            { "value": 7, "text": "Ruby" },
            { "value": 8, "text": "PHP" }
          ],
          "choicesOrder": "asc",
          "colCount": 3
        },
        {
          "type": "checkbox",
          "name": "devices-to-support",
          "title": "Which device types do you need to support?",
          "isRequired": true,
          "choices": [
            { "value": 1, "text": "Desktop" },
            { "value": 2, "text": "Tablet" },
            { "value": 3, "text": "Mobile" }
          ]
        }
      ]
    }
  ]
};
```

### `package.json`

```json
{
  "dependencies": {
    "react": "latest",
    "react-dom": "latest",
    "survey-core": "latest",
    "chart.js": "4.5.1",
    "survey-analytics": "latest"
  },
  "devDependencies": {
    "react-scripts": "latest"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "browserslist": [ ">0.2%", "not dead", "not ie <= 11", "not op_mini all" ]
}
```

## Other Frameworks

- [Angular](https://surveyjs.io/dashboard/examples/save-dashboard-state-to-local-storage/angular.md)
- [Vue 3](https://surveyjs.io/dashboard/examples/save-dashboard-state-to-local-storage/vue3js.md)
- [jQuery](https://surveyjs.io/dashboard/examples/save-dashboard-state-to-local-storage/jquery.md)
- [Vanilla JS](https://surveyjs.io/dashboard/examples/save-dashboard-state-to-local-storage/vanillajs.md)
