- Lab
- Core Tech

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.

Path Info
Table of Contents
-
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 thenpm run dev
command within the Terminal -
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
andJSON.parse
.
Implementing useLocalStorage
Head over to
src/hooks/useLocalStorage.js
and implement theuseLocalStorage
hook. This hook should:- Initialize state from LocalStorage if available, otherwise use the provided initial value.
- Update LocalStorage whenever the state changes.
useLocalStorage Instructions
-
Initialize state from LocalStorage:
- In the
useState
initializer, try to read the value for the givenkey
fromlocalStorage
. - If a value exists, parse it with
JSON.parse
and use it as the initial state. - If no value exists, use the provided
initialValue
.
- In the
-
Update LocalStorage when state changes:
- In a
useEffect
, wheneverkey
orvalue
changes, save the new value tolocalStorage
usinglocalStorage.setItem
. - Convert the value to a string using
JSON.stringify
before saving.
- In a
-
Return the state and setter:
- Return
[value, setValue]
from your hook so it works likeuseState
.
- Return
-
Error handling (optional but recommended):
- Wrap your
localStorage
access intry/catch
blocks to avoid breaking the app if storage is unavailable or parsing fails. - Log errors to the console for debugging.
- Wrap your
Using useLocalStorage in Calculator
Now head to
src/components/Calculator.jsx
and use theuseLocalStorage
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
-
Import the hook:
At the top of your file, add:import { useLocalStorage } from '../hooks/useLocalStorage';
-
Replace useState for each field:
For each calculator input field, replaceuseState
withuseLocalStorage
, 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.
-
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
andJSON.parse
.
Implementing useSessionStorage
Head over to
src/hooks/useSessionStorage.js
and implement theuseSessionStorage
hook. This hook should:- Initialize state from SessionStorage if available, otherwise use the provided initial value.
- Update SessionStorage whenever the state changes.
useSessionStorage Instructions
-
Initialize state from SessionStorage:
- In the
useState
initializer, try to read the value for the givenkey
fromsessionStorage
. - If a value exists, parse it with
JSON.parse
and use it as the initial state. - If no value exists, use the provided
initialValue
.
- In the
-
Update SessionStorage when state changes:
- In a
useEffect
, wheneverkey
orvalue
changes, save the new value tosessionStorage
usingsessionStorage.setItem
. - Convert the value to a string using
JSON.stringify
before saving.
- In a
-
Return the state and setter:
- Return
[value, setValue]
from your hook so it works likeuseState
.
- Return
-
Error handling (optional but recommended):
- Wrap your
sessionStorage
access intry/catch
blocks to avoid breaking the app if storage is unavailable or parsing fails. - Log errors to the console for debugging.
- Wrap your
Using useSessionStorage in Calculator
Now head to
src/components/Calculator.jsx
and use theuseSessionStorage
hook to persist the View Mode field:- View Mode (simple/detailed)
Follow these steps:
Calculator Instructions
-
Import the hook:
At the top of your file, add:import { useSessionStorage } from '../hooks/useSessionStorage';
-
Replace useState for viewMode:
ReplaceuseState
for theviewMode
field withuseSessionStorage
, 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.
-
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:- Import Dexie and create a new database.
- Define a
history
table with the fields needed for each calculation. - Export the database instance for use in other components.
db.js Instructions
-
Import Dexie:
import Dexie from 'dexie';
-
Create the database:
export const db = new Dexie('factoryDB');
-
Define the schema:
db.version(1).stores({ history: '++id, timestamp, units, cost, defectChance, defectModifier, expectedCost' });
-
Export the instance:
- Make sure
db
is exported for use in your components.
- Make sure
-
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
- Import db from
src/db/db.js
. - 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
andHistory
as a prop.
- Use
Calculator.jsx Instructions
- Import db from
../db/db
. - On "Save to History":
- Define a
defectChance
variable set todefectChancePercent / 100
and adefectModifier
variable set to1 + (defectModifierPercent / 100)
. - Create a record with all calculation fields and a timestamp. Use the two variables you just defined.
- Add it to
db.history
.
- Define a
History.jsx Instructions
- Import db from
../db/db
. - Update handleDelete
- Add an
id
parameter to the asynchandleDelete
function - Add a
await db.history.delete(id);
call before therefreshHistory
call.
- Add an
Running and Interacting with the Application
To run your completed application:
- Open your terminal in the project root.
- Start the development server:
npm run dev
- 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.
- Import db from
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.