localStorage limits that surprise you in production

localStorage is sync, persistent, and per-origin — which makes it tempting for feature flags and draft posts until you hit roughly five megabytes and a main-thread stringify.

Quota and browser differences

Browsers enforce per-origin limits (often around 5–10 MB, implementation-dependent). Private mode may evict storage sooner. Mobile WebViews can be stricter than desktop Chrome.

Everything is one string map per origin — no namespaces beyond your key naming discipline. Giant JSON blobs for “offline cache” compete with analytics flags and UI prefs in the same bucket.

Quota errors surface as `DOMException` with name `QuotaExceededError`. Many apps never catch it, so users think “save broken” with no toast.

Patterns that blow the budget

Storing base64 images, full API responses, or serialized Redux state daily. Use IndexedDB or Cache API for large binary or structured data; reserve localStorage for small knobs.

Duplicating data already in memory “for refresh recovery” without TTL. Version your keys (`draft:v3:`) and migrate/delete old prefixes on app boot.

Logging entire objects with circular references — `JSON.stringify` throws and your save handler dies. Sanitize before persist.

Inspecting storage without DevTools archaeology

DevTools Application tab works, but support reps and PMs do not live there. A focused UI helps.

The Local Storage Manager on DroidXP lists keys and values for the current origin, supports edit and delete, and runs locally. Use it on staging with the same domain as prod (different subdomain = different origin).

Before shipping a big migration, export keys, clear stale experiments, measure byte size with `new Blob([value]).size` in a quick script.

Safer alternatives and guardrails

Try/catch every `setItem`, surface human-readable errors, and degrade gracefully (download export, email draft link).

For secrets, never localStorage — HttpOnly cookies or server session. XSS can read storage; do not store refresh tokens there.

Document key ownership in your frontend README so the next feature does not add a second “source of truth” beside your API.