$ snippet localstorage-with-expiry
localStorage with expiry and JSON
A tiny wrapper that stores objects, expires them after a TTL, and doesn't crash in private browsing or when the quota is full.
const store = {
/** store.set("session", { id: 1 }, 60 * 60 * 1000) — expires in 1 hour (omit ttl for no expiry) */
set(key, value, ttlMs) {
const record = { value, expires: ttlMs ? Date.now() + ttlMs : null };
try { localStorage.setItem(key, JSON.stringify(record)); return true; }
catch { return false; } // quota exceeded, private mode, or storage disabled
},
get(key, fallback = null) {
try {
const raw = localStorage.getItem(key);
if (raw === null) return fallback;
const { value, expires } = JSON.parse(raw);
if (expires && Date.now() > expires) { localStorage.removeItem(key); return fallback; }
return value;
} catch { return fallback; } // corrupt JSON or storage unavailable
},
remove(key) { try { localStorage.removeItem(key); } catch {} },
};How it works
Raw localStorage only stores strings, never expires anything, and throws in several real situations — Safari private mode in older versions, a full quota, or a browser setting that blocks storage. Wrapping every call in try and returning a fallback keeps the page working when storage doesn't. The expiry is checked lazily on read, which is enough for caches and "don't show this again for a week" flags.
Gotchas
- Storage is per origin and synchronous; large values (hundreds of KB) block the main thread. For big or structured data use IndexedDB.
- Never store secrets or tokens you would not want any script on the page to read —
localStorageis fully exposed to XSS. JSON.stringifydropsundefined, functions andDateobjects (dates become strings). Store timestamps as numbers.
Ad slot