localStorage in JavaScript: Why you should stop using local storage today
- 👤 Andrés Cruz
If you are building a modern web application today and still relying on localStorage in JavaScript as a storage solution, you are probably accumulating problems that you don't see yet… but they will arrive.
localStorage appeared around 2009 and, although it remains widely documented and taught, it has not evolved at the pace of the modern web. And that matters more than many tutorials admit.
Previously we saw how to use Ambient Light events with JavaScript using AmbientLightSensor
What is localStorage in JavaScript (and why it keeps appearing in all tutorials)
localStorage is part of the Web Storage API and allows you to save key-value pairs in the user's browser persistently. Unlike sessionStorage, the data is not deleted when the tab or browser is closed.
The problem is not what it is, but why it is still recommended without context.
How local storage really works in the browser
- Data is saved by origin (protocol + domain).
- Everything is stored as UTF-16 strings.
- Operations are synchronous.
- There is no concurrency control, transactions, or isolation.
This might sound acceptable in small examples. In real applications, it is not so much.
localStorage vs sessionStorage: differences that do matter
Both share almost all limitations. The main difference is the duration. Neither solves modern problems like concurrency, structured data, or performance under load.
The big problem with localStorage: it only stores strings
This is the origin of many silent errors.
localStorage does not understand objects, arrays, or types. Everything goes through manual serialization. And this is where the bugs begin.
Serialization and deserialization: the origin of silent bugs
In my experience, a surprising amount of corrupt states in production came from poor management of JSON.stringify() and JSON.parse().
Values like "true", "false", "null", "" or even poorly versioned data end up causing unpredictable behavior. The result is usually the same:
“clear the cache and try again.”
As a customer, in the last year three different services gave me exactly that solution. It wasn't magic: it was poorly managed localStorage.
localStorage does not use structured data (and that leaves it in the past)
Modern JavaScript uses the StructuredClone algorithm to transfer data safely and consistently.
APIs such as:
- IndexedDB
- Web Workers
- postMessage
- Cache API
- BroadcastChannel
use structured data without manual serialization.
localStorage does not.
When you work with these modern APIs and go back to localStorage, it feels like going back a decade. And worst of all: there are no plans to update it.
Real security problems with localStorage
This is not theory. It is common practice… and dangerous.
Why you should never save tokens, JWTs, or sessions
I have seen it too many times:
- JWT
- Session IDs
- API keys
saved in localStorage “because it's easy.”
localStorage is accessible from any script running on your page. An XSS is enough to exfiltrate everything. And since the data is persistent, the impact is multiplied.
You should never store sensitive data in localStorage.
Performance and main thread blocking
localStorage is synchronous. Every read and write blocks the main thread.
Synchronous operations and their impact on UX
In applications with frequent state changes, this directly affects:
- animations
- scrolling
- user interaction
On low-power devices, the problem is exacerbated. I have seen stuttering interfaces simply from reading and writing too much to localStorage.
Asynchrony is not a luxury. It is a requirement for fluid applications.
Structural limitations: concurrency, atomicity, and scalability
localStorage:
- Has no transactions
- Does not guarantee atomicity
- Does not offer isolation
- Has no locking mechanisms
localStorage and the impossibility of using Web Workers
You cannot access localStorage from Web Workers. That leaves it out of any serious concurrency strategy.
It is not designed to scale. And it never was.
Storage limits and data lifecycle management
The famous ~5 MB limit is still real (and varies by browser).
The real limit of localStorage (and the risk of eviction)
That space is:
- small for modern apps
- subject to eviction
- your responsibility to manage
The browser does not warn you. You must handle failures, cleanup, and recovery. Something almost no one does well.
A brief history of browser storage: cookies, WebSQL, and repeated mistakes
Before localStorage, we had cookies. Then WebSQL.
Why WebSQL died
- Implementation limited to few browsers
- Lack of W3C standardization
- Direct competition with IndexedDB
- Security concerns
WebSQL was much loved… and yet it died.
The parallelism with localStorage is evident: popular, convenient technology, but poorly aligned with the future.
Why IndexedDB is a real alternative to localStorage
IndexedDB is not perfect. But it solves the important problems.
Asynchronous storage and structured data
- Asynchronous API (does not block the thread)
- Structured data (StructuredClone)
- Much higher quotas
- Greater reliability
Not because IndexedDB is perfect, but because it fits how web applications work today.
The IndexedDB API is bad (but the concept is correct)
Let's be honest: the native IndexedDB API is clunky, event-based, and verbose.
That's why it makes sense to use lightweight libraries.
Why using a lightweight library makes sense
Good libraries:
- use Promises
- reduce boilerplate code
- avoid common mistakes
Personally, I don't recommend huge libraries. For many cases, you don't need complex versioning or advanced cursors. I even created a small library focused only on the essentials, because most solutions were oversized.
When does it make sense to use localStorage?
To be fair: there are valid cases.
Simple cases where localStorage is not a problem
- UI flags
- non-critical preferences
- trivial data with no impact on security or performance
What you should never save, even "for convenience"
- tokens
- sessions
- complex state
- data that affects critical logic
There, localStorage stops being a simple solution and becomes technical debt.
Conclusion: localStorage is not the future of storage in JavaScript
localStorage continues to be taught because it is easy to explain.
But ease does not equal good architecture.
New developers gain much more from learning:
- asynchrony
- Promises
- structured data
- IndexedDB
than trying to understand why "0" breaks a condition or why a user gets null data after an update.
If you can avoid localStorage today, do it.
And if you can't, at least use it knowing exactly what sacrifices you are making.
Frequently Asked Questions about localStorage in JavaScript (FAQ)
Is localStorage secure?
Not for sensitive data. It is vulnerable to XSS and accessible from any script.
How much space does localStorage allow?
Approximately 5 MB, depending on the browser, and subject to eviction.
Is localStorage synchronous or asynchronous?
It is completely synchronous and blocks the main thread.
Does IndexedDB replace localStorage?
For most modern applications, yes, it should.
Does localStorage block the main thread?
Yes. Every read and write operation.
Technical Comparison: localStorage vs. IndexedDB
| Feature | localStorage | IndexedDB |
|---|---|---|
| Data Type | Strings only (UTF-16) | Objects, Blobs, Arrays, etc. |
| Capacity | ~5 MB (rigid) | % of disk (dynamic, much larger) |
| Operation Mode | Synchronous (Blocks UI) | Asynchronous (Does not block) |
| Security (XSS) | Very vulnerable | Vulnerable (but allows isolation) |
| Web Workers | Not available | Compatible |
| Transactions | No | Yes (Guarantees integrity) |
I agree to receive announcements of interest about this Blog.
Discover why this synchronous API is harming the performance and security of your modern web applications. We analyze its risks compared to IndexedDB and how to properly manage structured data in JavaScript.