- Lab
-
Libraries: If you want this lab, consider one of these libraries.
- 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.
Lab 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
solutionsdirectory which you can reference at any time to check your implementation. You can run the project as you progress with thenpm run devcommand 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.stringifyandJSON.parse.
Implementing useLocalStorage
Head over to
src/hooks/useLocalStorage.jsand implement theuseLocalStoragehook. 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
useStateinitializer, try to read the value for the givenkeyfromlocalStorage. - If a value exists, parse it with
JSON.parseand use it as the initial state. - If no value exists, use the provided
initialValue.
- In the
-
Update LocalStorage when state changes:
- In a
useEffect, wheneverkeyorvaluechanges, save the new value tolocalStorageusinglocalStorage.setItem. - Convert the value to a string using
JSON.stringifybefore 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
localStorageaccess intry/catchblocks 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.jsxand use theuseLocalStoragehook 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, replaceuseStatewithuseLocalStorage, 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.stringifyandJSON.parse.
Implementing useSessionStorage
Head over to
src/hooks/useSessionStorage.jsand implement theuseSessionStoragehook. 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
useStateinitializer, try to read the value for the givenkeyfromsessionStorage. - If a value exists, parse it with
JSON.parseand use it as the initial state. - If no value exists, use the provided
initialValue.
- In the
-
Update SessionStorage when state changes:
- In a
useEffect, wheneverkeyorvaluechanges, save the new value tosessionStorageusingsessionStorage.setItem. - Convert the value to a string using
JSON.stringifybefore 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
sessionStorageaccess intry/catchblocks 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.jsxand use theuseSessionStoragehook 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:
ReplaceuseStatefor theviewModefield 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.jsand implement the Dexie database instance. This file should:- Import Dexie and create a new database.
- Define a
historytable 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
dbis 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
refreshHistoryfunction:- 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
CalculatorandHistoryas a prop.
- Use
Calculator.jsx Instructions
- Import db from
../db/db. - On "Save to History":
- Define a
defectChancevariable set todefectChancePercent / 100and adefectModifiervariable 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
idparameter to the asynchandleDeletefunction - Add a
await db.history.delete(id);call before therefreshHistorycall.
- 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
About the author
Real skill practice before real-world application
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.
Learn by doing
Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.
Follow your guide
All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.
Turn time into mastery
On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.