Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.

Use Web Storage API safely

Use localStorage and sessionStorage with proper serialization, error handling, and security awareness to avoid data corruption and storage quota errors.

Utilities
Quick take
Typical fix time 15 min
  • Always wrap localStorage access in try/catch — storage can be full or disabled
  • Serialize objects with JSON.stringify and validate on read with JSON.parse
  • Never store sensitive data (tokens, passwords) in localStorage — use httpOnly cookies
  • Use sessionStorage for tab-scoped data that shouldn't persist
Why it matters: localStorage and sessionStorage have a 5–10 MB quota per origin, and any write can throw a QuotaExceededError. Private browsing modes in some browsers disable storage entirely and throw on all writes. Applications that don't handle these errors crash silently, leaving users unable to use the app.

Rule Details

Web Storage provides a simple synchronous key-value store, but its synchronous nature and quota limits require defensive coding.

Code Example

// A defensive localStorage wrapper
const storage = {
  get(key, fallback = null) {
    try {
      const item = localStorage.getItem(key)
      if (item === null) return fallback
      return JSON.parse(item)
    } catch {
      return fallback
    }
  },
 
  set(key, value) {
    try {
      localStorage.setItem(key, JSON.stringify(value))
      return true
    } catch (error) {
      if (error instanceof DOMException && error.name === 'QuotaExceededError') {
        console.warn('Storage quota exceeded')
      }
      return false
    }
  },
 
  remove(key) {
    try {
      localStorage.removeItem(key)
    } catch { /* ignore */ }
  }
}
 
// Usage
storage.set('user-preferences', { theme: 'dark', fontSize: 16 })
const prefs = storage.get('user-preferences', { theme: 'light', fontSize: 14 })

Why It Matters

localStorage and sessionStorage have a 5–10 MB quota per origin, and any write can throw a QuotaExceededError. Private browsing modes in some browsers disable storage entirely and throw on all writes. Applications that don't handle these errors crash silently, leaving users unable to use the app.

localStorage vs sessionStorage

// localStorage — persists across browser sessions (until cleared)
localStorage.setItem('theme', 'dark')
// User closes browser, reopens: theme is still 'dark'
 
// sessionStorage — cleared when the tab is closed
sessionStorage.setItem('draft-post', JSON.stringify(postContent))
// Tab closes: draft is gone
// New tab: cannot see the previous tab's sessionStorage

What NOT to Store

// ❌ Never store security-sensitive data in localStorage
localStorage.setItem('auth-token', jwtToken)
// Any XSS attack on any script on your site can read this!
 
// ❌ Never store passwords, credit card numbers, PII
localStorage.setItem('user-password', password)
 
// ✅ Authentication tokens belong in httpOnly cookies (set by the server)
// The browser sends them automatically and JS cannot read them
 
// ✅ Fine to store: UI preferences, cached non-sensitive data, feature flags
localStorage.setItem('sidebar-collapsed', 'true')
localStorage.setItem('last-viewed-category', 'javascript')

Quota Management

// Check available space (not universally supported but useful)
function estimateStorageUsage() {
  let total = 0
  for (const key of Object.keys(localStorage)) {
    total += localStorage.getItem(key)?.length ?? 0
  }
  return `~${(total / 1024).toFixed(2)} KB used`
}
 
// Evict old cache entries when quota is exceeded
function setWithEviction(key, value) {
  try {
    localStorage.setItem(key, JSON.stringify(value))
  } catch {
    // Remove oldest cached items and retry
    const cacheKeys = Object.keys(localStorage).filter(k => k.startsWith('cache:'))
    if (cacheKeys.length > 0) {
      localStorage.removeItem(cacheKeys[0])
      localStorage.setItem(key, JSON.stringify(value))
    }
  }
}

Exceptions

  • A framework default or browser behavior is not an exception by itself; only documented constraints with compensating controls should suppress the finding.
  • When a JavaScript pattern looks unsafe but the data is fully constrained, validated, and never attacker-controlled, document that boundary explicitly instead of treating it as implicit.
  • If a rule overlaps with a stronger exploit path or runtime failure, fix the issue that most directly enables compromise or user-visible breakage first.

Verification

  1. Verify the behavior in the browser after the code change, not only in static analysis.
  2. Inspect DevTools Network or Performance panels when the rule affects loading or execution order.
  3. Test the primary user flow and one edge case triggered by the changed script path.
  4. Confirm the code still behaves correctly when the feature is delayed, lazy-loaded, or fails.

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Find all localStorage and sessionStorage accesses in this file. Check for missing try/catch, unhandled JSON parsing, and any storage of sensitive data.

Fix

Auto-fix issues

Add try/catch error handling to all Web Storage operations and ensure all object values are properly serialized and deserialized.

Explain

Learn more

Explain the Web Storage API, its limitations, when to use localStorage vs sessionStorage, and what data should never be stored there.

Review

Code review

Review scripts, client components, and browser execution paths related to Use Web Storage API safely. Flag exact imports, event handlers, runtime side effects, or blocking operations that violate the rule, and state how the change should be verified in the browser.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

MDN Web Storage APIdeveloper.mozilla.orgTool

Rules that often go hand-in-hand with this one.

Parse JSON safely with error handling

Always wrap JSON.parse() in try/catch and validate the parsed structure before use, as invalid JSON or unexpected data shapes cause runtime errors.

JavaScript
Handle cross-origin requests securely

Use CORS correctly, validate message origins with postMessage, and understand the Same-Origin Policy to prevent cross-origin attacks.

JavaScript
Implement proper error handling

Use try-catch blocks and error boundaries to gracefully handle errors in async operations and UI components.

JavaScript

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385