localStorage stores text in the user's browser, per website, and keeps it after the page is closed. sessionStorage works the same way but is cleared when the tab closes. Both hold only strings, so convert objects with JSON.stringify and back with JSON.parse.
Stored data can be missing, outdated or corrupted, and storage can be blocked (private browsing, full quota). Wrap reads and writes in try/catch and fall back to sensible defaults.
Use localStorage for preferences, drafts and small app data. Never store passwords, tokens or personal data there — any script on the page can read it.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Preferences</title>
<style>
body.dark { background: #0e1826; color: #e7ecf6; }
</style>
<script src="app.js" defer></script>
</head>
<body>
<button id="theme">Toggle dark mode</button>
<label>Draft note <textarea id="draft" rows="3"></textarea></label>
<p id="visits"></p>
</body>
</html>const KEY = "dolese.prefs.v1";
const DEFAULTS = { theme: "light", draft: "", visits: 0 };
function loadPrefs() {
try {
const raw = localStorage.getItem(KEY);
return raw ? { ...DEFAULTS, ...JSON.parse(raw) } : { ...DEFAULTS };
} catch {
return { ...DEFAULTS }; // corrupted JSON or storage blocked
}
}
function savePrefs(prefs) {
try {
localStorage.setItem(KEY, JSON.stringify(prefs));
} catch (err) {
console.warn("Could not save preferences:", err);
}
}
const prefs = loadPrefs();
prefs.visits += 1;
savePrefs(prefs);
const draft = document.querySelector("#draft");
document.body.classList.toggle("dark", prefs.theme === "dark");
draft.value = prefs.draft;
document.querySelector("#visits").textContent = `You have visited ${prefs.visits} time(s).`;
document.querySelector("#theme").addEventListener("click", () => {
prefs.theme = prefs.theme === "dark" ? "light" : "dark";
document.body.classList.toggle("dark", prefs.theme === "dark");
savePrefs(prefs);
});
draft.addEventListener("input", () => {
prefs.draft = draft.value;
savePrefs(prefs);
});Key points
- localStorage keeps strings per site; use JSON to store objects.
- Always handle missing or corrupted data and blocked storage.
- Never store secrets or personal data in localStorage.
Exercise
Add a "font size" setting (small / normal / large) saved in preferences. Add a "Reset preferences" button that clears the key with localStorage.removeItem and reloads the defaults.
Show solution
Try the exercise yourself first — then compare your approach with this one.
The font size becomes another preference: a <select> changes it, a class on <body> applies it, and it's saved with the others. Reset removes the stored key, restores the defaults and re-applies them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Preferences</title>
<style>
body.dark { background: #0e1826; color: #e7ecf6; }
body.size-small { font-size: 14px; }
body.size-normal { font-size: 16px; }
body.size-large { font-size: 20px; }
</style>
<script src="app.js" defer></script>
</head>
<body>
<button id="theme">Toggle dark mode</button>
<label>Font size
<select id="font-size">
<option value="small">Small</option>
<option value="normal">Normal</option>
<option value="large">Large</option>
</select>
</label>
<button id="reset">Reset preferences</button>
</body>
</html>const KEY = "dolese.prefs.v1";
const DEFAULTS = { theme: "light", fontSize: "normal" };
function loadPrefs() {
try {
return { ...DEFAULTS, ...JSON.parse(localStorage.getItem(KEY) ?? "{}") };
} catch {
return { ...DEFAULTS };
}
}
function savePrefs() {
try {
localStorage.setItem(KEY, JSON.stringify(prefs));
} catch (err) {
console.warn("Could not save preferences:", err);
}
}
function apply() {
document.body.classList.toggle("dark", prefs.theme === "dark");
document.body.classList.remove("size-small", "size-normal", "size-large");
document.body.classList.add(`size-${prefs.fontSize}`);
fontSelect.value = prefs.fontSize;
}
const fontSelect = document.querySelector("#font-size");
let prefs = loadPrefs();
apply();
document.querySelector("#theme").addEventListener("click", () => {
prefs.theme = prefs.theme === "dark" ? "light" : "dark";
savePrefs();
apply();
});
fontSelect.addEventListener("change", () => {
prefs.fontSize = fontSelect.value;
savePrefs();
apply();
});
document.querySelector("#reset").addEventListener("click", () => {
localStorage.removeItem(KEY);
prefs = { ...DEFAULTS };
apply();
});