Featured resource
Tech Upskilling Playbook 2025
Tech Upskilling Playbook

Build future-ready tech teams and hit key business milestones with seven proven plays from industry leaders.

Learn more
  • Labs icon Lab
  • Core Tech
Labs

Guided: Client-side Data Persistence

In this Code-Lab, you'll modify a React application that demonstrates real-world use of localStorage, sessionStorage, and IndexedDB (via Dexie.js). You'll implement persistent user inputs, manage session-based state, and design an offline-capable data layer for storing structured records.

Labs

Path Info

Level
Clock icon Intermediate
Duration
Clock icon 30m
Last updated
Clock icon Aug 22, 2025

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Table of Contents

  1. Challenge

    Introduction

    Overview

    This Code-Lab lab explores client-side data persistence in modern web applications, including:

    • LocalStorage for persisting user input across sessions
    • SessionStorage for storing temporary state within a browser tab
    • IndexedDB for managing structured, queryable data such as calculation history

    Scenario

    For this lab, you will start with a React project bootstrapped with Vite. Your task is to build a factory cost calculator that allows users to input production parameters, view calculated results, and save their calculation history. The application will demonstrate how to use different browser storage APIs to persist user data, preferences, and history, ensuring a seamless experience even after page reloads or browser restarts.


    Additional Info

    There is a solutions directory which you can reference at any time to check your implementation. You can run the project as you progress with the npm run dev command within the Terminal

  2. Challenge

    LocalStorage

    At the core of this CodeLab is the use of LocalStorage, which provides a simple and persistent way to store user input and preferences in the browser. By leveraging LocalStorage, your calculator can remember user data across sessions, improving usability and user experience.

    LocalStorage allows you to store key-value pairs as strings. Data saved here remains available even after the browser is closed and reopened, making it ideal for persisting calculator fields such as units per hour, cost per unit, and defect parameters.


    LocalStorage API

    Here’s how you interact with LocalStorage in JavaScript:

    // Save a value
    localStorage.setItem('units', JSON.stringify(10));
    
    // Retrieve a value
    const units = JSON.parse(localStorage.getItem('units'));
    

    LocalStorage is synchronous and only supports string data, so you must serialize and deserialize objects using JSON.stringify and JSON.parse.


    Implementing useLocalStorage

    Head over to src/hooks/useLocalStorage.js and implement the useLocalStorage hook. This hook should:

    1. Initialize state from LocalStorage if available, otherwise use the provided initial value.
    2. Update LocalStorage whenever the state changes.
    useLocalStorage Instructions
    1. Initialize state from LocalStorage:

      • In the useState initializer, try to read the value for the given key from localStorage.
      • If a value exists, parse it with JSON.parse and use it as the initial state.
      • If no value exists, use the provided initialValue.
    2. Update LocalStorage when state changes:

      • In a useEffect, whenever key or value changes, save the new value to localStorage using localStorage.setItem.
      • Convert the value to a string using JSON.stringify before saving.
    3. Return the state and setter:

      • Return [value, setValue] from your hook so it works like useState.
    4. Error handling (optional but recommended):

      • Wrap your localStorage access in try/catch blocks to avoid breaking the app if storage is unavailable or parsing fails.
      • Log errors to the console for debugging.

    Using useLocalStorage in Calculator

    Now head to src/components/Calculator.jsx and use the useLocalStorage hook to persist the following fields:

    • Units per hour
    • Cost per unit
    • Chance of defect (%)
    • Defect cost modifier (%)

    This will ensure that when users reload or revisit the page, their previous input is automatically restored.

    Calculator Instructions
    1. Import the hook:
      At the top of your file, add:

      import { useLocalStorage } from '../hooks/useLocalStorage';
      
    2. Replace useState for each field:
      For each calculator input field, replace useState with useLocalStorage, passing a unique key and a sensible default value.
      Example:

      const [units, setUnits] = useLocalStorage("units", 10);
      const [cost, setCost] = useLocalStorage("cost", 5);
      const [defectChancePercent, setDefectChancePercent] = useLocalStorage("defectChance", 0.1);
      const [defectModifierPercent, setDefectModifierPercent] = useLocalStorage("defectModifier", 1.3);
      

    Next Steps

    Once you have LocalStorage working for your calculator inputs, you’ll move on to SessionStorage and IndexedDB in the following steps.

  3. Challenge

    SessionStorage

    In this step of the CodeLab, you’ll implement SessionStorage to persist temporary state within a browser tab. SessionStorage is ideal for storing data that should only last for the duration of a single tab or session, such as UI preferences that don’t need to survive a full browser restart.

    SessionStorage allows you to store key-value pairs as strings, similar to LocalStorage, but the data is cleared when the tab is closed.


    SessionStorage API

    Here’s how you interact with SessionStorage in JavaScript:

    // Save a value
    sessionStorage.setItem('viewMode', JSON.stringify('simple'));
    
    // Retrieve a value
    const viewMode = JSON.parse(sessionStorage.getItem('viewMode'));
    

    SessionStorage is synchronous and only supports string data just like LocalStorage, so you must serialize and deserialize objects using JSON.stringify and JSON.parse.


    Implementing useSessionStorage

    Head over to src/hooks/useSessionStorage.js and implement the useSessionStorage hook. This hook should:

    1. Initialize state from SessionStorage if available, otherwise use the provided initial value.
    2. Update SessionStorage whenever the state changes.
    useSessionStorage Instructions
    1. Initialize state from SessionStorage:

      • In the useState initializer, try to read the value for the given key from sessionStorage.
      • If a value exists, parse it with JSON.parse and use it as the initial state.
      • If no value exists, use the provided initialValue.
    2. Update SessionStorage when state changes:

      • In a useEffect, whenever key or value changes, save the new value to sessionStorage using sessionStorage.setItem.
      • Convert the value to a string using JSON.stringify before saving.
    3. Return the state and setter:

      • Return [value, setValue] from your hook so it works like useState.
    4. Error handling (optional but recommended):

      • Wrap your sessionStorage access in try/catch blocks to avoid breaking the app if storage is unavailable or parsing fails.
      • Log errors to the console for debugging.

    Using useSessionStorage in Calculator

    Now head to src/components/Calculator.jsx and use the useSessionStorage hook to persist the View Mode field:

    • View Mode (simple/detailed)

    Follow these steps:

    Calculator Instructions
    1. Import the hook:
      At the top of your file, add:

      import { useSessionStorage } from '../hooks/useSessionStorage';
      
    2. Replace useState for viewMode:
      Replace useState for the viewMode field with useSessionStorage, passing a unique key (e.g., "viewMode") and a sensible default value (e.g., "simple"). Example:

      const [viewMode, setViewMode] = useSessionStorage("viewMode", "simple");
      

    Next Steps

    Once you have SessionStorage working for your calculator’s view mode, you’ll move on to IndexedDB for calculation history in the next step.

  4. Challenge

    IndexedDB with Dexie

    In this final step of the CodeLab, you’ll implement IndexedDB using the Dexie library to persist and manage calculation history. IndexedDB is a browser database for storing larger amounts of structured data than Local or Session storage, and Dexie provides a simple API for interacting with it in React.

    By the end of this step, your calculator will allow users to save, view, and delete their calculation history, with all data stored locally in the browser—even across page reloads and browser restarts.


    IndexedDB & Dexie API

    Dexie wraps IndexedDB and lets you define tables and interact with them using JavaScript:

    import Dexie from 'dexie';
    
    export const db = new Dexie('factoryDB');
    db.version(1).stores({
      history: '++id, timestamp, units, cost, defectChance, defectModifier, expectedCost'
    });
    

    You can add, query, and delete records:

    // Add a record
    await db.history.add({ units, cost, ... });
    
    // Get all records
    const all = await db.history.toArray();
    
    // Delete a record
    await db.history.delete(id);
    

    Implementing IndexedDB with Dexie

    Head over to src/db/db.js and implement the Dexie database instance. This file should:

    1. Import Dexie and create a new database.
    2. Define a history table with the fields needed for each calculation.
    3. Export the database instance for use in other components.
    db.js Instructions
    1. Import Dexie:

      import Dexie from 'dexie';
      
    2. Create the database:

      export const db = new Dexie('factoryDB');
      
    3. Define the schema:

      db.version(1).stores({
        history: '++id, timestamp, units, cost, defectChance, defectModifier, expectedCost'
      });
      
    4. Export the instance:

      • Make sure db is exported for use in your components.
  5. Challenge

    Updating App Logic

    Using IndexedDB in App and Calculator

    Now update your main app logic to use Dexie for calculation history.

    App.jsx Instructions
    1. Import db from src/db/db.js.
    2. Implement a refreshHistory function:
      • Use db.history.toArray() to get all records.
      • Sort by timestamp with sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp)).
      • Store in state.
      • Fetch and update history state.
      • Pass this function to Calculator and History as a prop.
    Calculator.jsx Instructions
    1. Import db from ../db/db.
    2. On "Save to History":
      • Define a defectChance variable set to defectChancePercent / 100 and a defectModifier variable set to 1 + (defectModifierPercent / 100).
      • Create a record with all calculation fields and a timestamp. Use the two variables you just defined.
      • Add it to db.history.
    History.jsx Instructions
    1. Import db from ../db/db.
    2. Update handleDelete
      • Add an id parameter to the async handleDelete function
      • Add a await db.history.delete(id); call before the refreshHistory call.

    Running and Interacting with the Application

    To run your completed application:

    1. Open your terminal in the project root.
    2. Start the development server:
      npm run dev
      
    3. Open your browser to the provided local URL (usually http://localhost:5173).

    How to interact:

    • Enter factory parameters in the calculator fields.
    • Switch between "Simple" and "Detailed" view modes.
    • Click "Save to History" to store your calculation.
    • View your calculation history below the calculator.
    • Delete individual history entries using the "Delete" button.
    • All data is stored locally in your browser—inputs and preferences persist across reloads due to LocalStorage, and history is saved using IndexedDB. The view mode will reset if the browser tab is closed as per SessionStorage.

    Great job! You’ve built a fully persistent, client-side React application using LocalStorage, SessionStorage, and IndexedDB.

George is a Pluralsight Author working on content for Hands-On Experiences. He is experienced in the Python, JavaScript, Java, and most recently Rust domains.

What's a lab?

Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.

Provided environment for hands-on practice

We will provide the credentials and environment necessary for you to practice right within your browser.

Guided walkthrough

Follow along with the author’s guided walkthrough and build something new in your provided environment!

Did you know?

On average, you retain 75% more of your learning if you get time for practice.