⚡ localStorage Isn’t Always There — Code Like It Might Not Be
Code that calls localStorage.setItem() directly works perfectly in normal testing, then throws an uncaught exception and breaks the whole page for users in Safari’s Private Browsing mode, some locked-down corporate browsers, or with storage explicitly disabled.
🐞 The Problem
function saveDraft(text) {
localStorage.setItem('draft', text); // throws in some environments
}
// Safari Private Browsing (older versions), some strict privacy
// settings, or storage quota exceeded: this throws a
// QuotaExceededError or SecurityError - and since it's not
// wrapped, it can break the entire calling function silently.
🔍 Why This Happens
Several real-world situations make localStorage unavailable or throw on write: Private/Incognito browsing in some browser versions, storage quota genuinely exceeded, browser settings that block storage entirely, or the code running inside a sandboxed iframe with storage access restricted. None of these are edge cases you can assume away for a public-facing site.
✅ The Fix: Feature-Detect and Wrap in try/catch
- Check availability once, and always wrap actual reads/writes defensively — availability can change mid-session (e.g. quota fills up).
- Fall back gracefully: an in-memory variable, a cookie, or simply not persisting the draft is far better than a broken page.
🛡️ Safe Wrapper
function isStorageAvailable() {
try {
const testKey = '__storage_test__';
localStorage.setItem(testKey, testKey);
localStorage.removeItem(testKey);
return true;
} catch {
return false;
}
}
function saveDraft(text) {
try {
localStorage.setItem('draft', text);
} catch (err) {
console.warn('localStorage unavailable, draft not persisted:', err);
// Fall back to an in-memory variable so the feature still
// works for this session, just without persistence.
inMemoryDraft = text;
}
}
⚠️ Same Applies to sessionStorage and IndexedDB
- Any browser storage API can be unavailable or throw under similar conditions — the defensive pattern (feature-detect + try/catch + fallback) applies to all of them, not just localStorage.
Storage APIs feel as reliable as a regular variable, but they’re really talking to something outside your code’s control — treating every call as something that CAN fail is what keeps one user’s private browser tab from taking down your whole page.
