Learning objectives
Use localStorage defensively; serialize a versioned record; explain the limits of browser-only progress and avoid sensitive storage.
Prerequisites
Understand JavaScript objects, JSON, try/catch, and same-origin boundaries.
Choose the right persistence promise
Local storage stores string values for an origin and commonly survives page reloads and browser restarts. Session storage is scoped to a page session and is useful for shorter-lived state. Neither automatically synchronizes with a WordPress account or another device. A site should describe guest progress as saved on this browser rather than implying a durable cloud backup.
Storage can be unavailable, full, cleared by the user, or affected by private-browsing and browser policies. A successful setItem call today does not mean the value will exist forever. Design the task so a failed save does not break the page, and provide a clear way to reset locally stored preferences.
Treat stored values as untrusted input
Web Storage stores strings, so structured data usually passes through JSON.stringify and JSON.parse. Parsing can fail if old code, an extension, or a user modifies the stored value. Wrap access and parsing in error handling, then validate the resulting shape before using it. A valid JSON array is not automatically the object your code expects.
Version your records when their shape may evolve. For example, a progress record can contain version, completed lesson identifiers, and updatedAt. Stable lesson identifiers are better than a page’s current title because titles can change. Keep migrations small and explicit; a broken migration should fall back to a usable empty state rather than prevent the lesson from loading.
Do not store passwords, session secrets, or sensitive personal notes in localStorage as a shortcut. Scripts running in the origin can access it, and an XSS flaw can expose its contents. Browser storage is a convenience for appropriate local state, not a confidential vault.
Keep the UI truthful across tabs and failures
The storage event can notify other same-origin documents when a storage area changes, but it is not fired on the exact document that made the change. Update the current UI directly after a successful operation. If several tabs write a whole progress object independently, a later write can overwrite newer values from another tab; consider merging state or using a more suitable persistence design.
Web Storage operations are synchronous, so avoid large data and frequent writes in tight input loops. IndexedDB is a different asynchronous database API for more complex local data, with its own design and error-handling requirements. A simple completion checkbox usually needs only a small versioned record.
Working example
<button type="button" id="save-progress">Save this lesson locally</button>
<button type="button" id="clear-progress">Clear this sample</button>
<p id="storage-status" role="status">No change has been made.</p>
Another example
<script>
function readProgress() {
try {
const value = JSON.parse(localStorage.getItem("demo-progress") || "null");
return value && value.version === 1 && Array.isArray(value.completed) ? value : { version: 1, completed: [] };
} catch {
return { version: 1, completed: [] };
}
}
</script>
Expected output
On a normal same-origin page, Save writes a small sample record and Clear removes it. In the isolated playground, opaque-origin storage may be unavailable; the example catches that condition and displays an explanation instead of throwing an uncaught exception.
Common mistakes
Do not show Saved before checking whether the write succeeded. Do not assume parsed JSON has the expected type. Do not erase all of localStorage with clear when you only own one feature key. Do not advertise device-local progress as account synchronization.
Accessibility and browser notes
Announce the result without moving focus away from the action. Keep clear/reset actions explicit about what they remove. A learner using a shared device should understand that another person using the same browser profile may see local progress.
Practice
Write a safe removal operation for only the key demo-progress, with success and failure feedback.
Review the solution and reasoning
try {
localStorage.removeItem("demo-progress");
status.textContent = "This sample’s local progress was cleared.";
} catch {
status.textContent = "Storage is unavailable; no change could be confirmed.";
}
Removing one owned key avoids deleting unrelated application state. The error message avoids claiming a change that could not be confirmed. In production, pair local data controls with an accurate privacy explanation.