localStorage en JavaScript: por qué deberías dejar de usar el almacenamiento local hoy

- 👤 Andrés Cruz

🇺🇸 In english

localStorage en JavaScript: por qué deberías dejar de usar el almacenamiento local hoy

Si hoy estás construyendo una aplicación web moderna y sigues apoyándote en localStorage en JavaScript como solución de almacenamiento, probablemente estés acumulando problemas que todavía no ves… pero que llegarán.

localStorage apareció alrededor de 2009 y, aunque sigue siendo ampliamente documentado y enseñado, no ha evolucionado al ritmo de la web moderna. Y eso importa más de lo que muchos tutoriales admiten.

Antes vimos como usar los eventos de Luz Ambiental con JavaScript con AmbientLightSensor

Qué es localStorage en JavaScript (y por qué sigue apareciendo en todos los tutoriales)

localStorage es parte de la Web Storage API y permite guardar pares clave-valor en el navegador del usuario de forma persistente. A diferencia de sessionStorage, los datos no se eliminan al cerrar la pestaña o el navegador.

El problema no es qué es, sino por qué se sigue recomendando sin contexto.

Cómo funciona realmente el almacenamiento local en el navegador

  • Los datos se guardan por origen (protocolo + dominio).
  • Todo se almacena como cadenas UTF-16.
  • Las operaciones son síncronas.
  • No hay control de concurrencia, transacciones ni aislamiento.

Esto puede sonar aceptable en ejemplos pequeños. En aplicaciones reales, no lo es tanto.

localStorage vs sessionStorage: diferencias que sí importan

Ambos comparten casi todas las limitaciones. La diferencia principal es la duración. Ninguno resuelve problemas modernos como concurrencia, datos estructurados o rendimiento bajo carga.

El gran problema de localStorage: solo almacena cadenas

Este es el origen de muchos errores silenciosos.

localStorage no entiende objetos, arrays ni tipos. Todo pasa por serialización manual. Y aquí empiezan los bugs.

Serialización y deserialización: el origen de bugs silenciosos

En mi experiencia, una cantidad sorprendente de estados corruptos en producción venían de una mala gestión de JSON.stringify() y JSON.parse().

Valores como "true", "false", "null", "" o incluso datos mal versionados acaban provocando comportamientos impredecibles. El resultado suele ser el mismo:

“borra la caché y vuelve a intentarlo”.

Como cliente, el último año tres servicios distintos me dieron exactamente esa solución. No era magia: era localStorage mal gestionado.

localStorage no usa datos estructurados (y eso lo deja en el pasado)

JavaScript moderno usa el algoritmo StructuredClone para transferir datos de forma segura y consistente.

APIs como:

  • IndexedDB
  • Web Workers
  • postMessage
  • Cache API
  • BroadcastChannel

usan datos estructurados sin serialización manual.

localStorage no.

Cuando trabajas con estas APIs modernas y vuelves a localStorage, se siente como retroceder una década. Y lo peor: no hay planes para actualizarlo.

Problemas reales de seguridad con localStorage

Esto no es teoría. Es práctica habitual… y peligrosa.

Por qué nunca deberías guardar tokens, JWT o sesiones

He visto demasiadas veces:

  • JWT
  • IDs de sesión
  • claves API
    guardadas en localStorage “porque es fácil”.

localStorage es accesible desde cualquier script que se ejecute en tu página. Un XSS es suficiente para exfiltrar todo. Y como los datos son persistentes, el impacto se multiplica.

Nunca deberías almacenar datos sensibles en localStorage.


Rendimiento y bloqueo del hilo principal

localStorage es síncrono. Cada lectura y escritura bloquea el main thread.

Operaciones síncronas y su impacto en la UX

En aplicaciones con estado frecuente, esto afecta directamente a:

  • animaciones
  • scroll
  • interacción del usuario

En dispositivos de baja potencia, el problema se agrava. He visto interfaces entrecortadas simplemente por leer y escribir demasiado en localStorage.

La asincronía no es un lujo. Es un requisito para aplicaciones fluidas.

Limitaciones estructurales: concurrencia, atomicidad y escalabilidad

localStorage:

  • No tiene transacciones
  • No garantiza atomicidad
  • No ofrece aislamiento
  • No tiene mecanismos de bloqueo

localStorage y la imposibilidad de usar Web Workers

No puedes acceder a localStorage desde Web Workers. Eso lo deja fuera de cualquier estrategia seria de concurrencia.

No está diseñado para escalar. Y nunca lo estuvo.

Límites de almacenamiento y gestión del ciclo de vida de los datos

El famoso límite de ~5 MB sigue siendo real (y variable según navegador).

El límite real de localStorage (y el riesgo de desalojo)

Ese espacio es:

  • pequeño para apps modernas
  • sujeto a desalojo
  • responsabilidad tuya de gestionar

El navegador no te avisa. Tú debes manejar fallos, limpieza y recuperación. Cosa que casi nadie hace bien.

Breve historia del almacenamiento en el navegador: cookies, WebSQL y errores repetidos

Antes de localStorage, tuvimos cookies. Luego WebSQL.

Por qué WebSQL murió

  • Implementación limitada a pocos navegadores
  • Falta de estandarización W3C
  • Competencia directa con IndexedDB
  • Preocupaciones de seguridad

WebSQL fue muy querido… y aun así murió.

El paralelismo con localStorage es evidente: tecnología popular, cómoda, pero mal alineada con el futuro.

Por qué IndexedDB es una alternativa real a localStorage

IndexedDB no es perfecto. Pero resuelve los problemas importantes.

Almacenamiento asíncrono y datos estructurados

  • API asíncrona (no bloquea el hilo)
  • Datos estructurados (StructuredClone)
  • Cuotas mucho mayores
  • Mayor fiabilidad

No porque IndexedDB sea perfecto, sino porque encaja con cómo funcionan hoy las aplicaciones web.

La API de IndexedDB es mala (pero el concepto es correcto)

Seamos honestos: la API nativa de IndexedDB es incómoda, basada en eventos y verbosa.

Por eso tiene sentido usar librerías ligeras.

Por qué usar una librería ligera tiene sentido

Las buenas librerías:

  • usan Promesas
  • reducen código repetitivo
  • evitan errores comunes

Personalmente, no recomiendo librerías enormes. Para muchos casos, no necesitas versionado complejo ni cursores avanzados. Incluso creé una pequeña librería enfocada solo en lo esencial, porque la mayoría de soluciones estaban sobredimensionadas.

¿Cuándo sí tiene sentido usar localStorage?

Para ser justos: sí hay casos válidos.

Casos simples donde localStorage no es un problema

  • flags de UI
  • preferencias no críticas
  • datos triviales sin impacto en seguridad o rendimiento

Lo que nunca deberías guardar, ni siquiera “por comodidad”

  • tokens
  • sesiones
  • estado complejo
  • datos que afectan lógica crítica

Ahí, localStorage deja de ser una solución simple y se convierte en deuda técnica.

Conclusión: localStorage no es el futuro del almacenamiento en JavaScript

localStorage sigue enseñándose porque es fácil de explicar.
Pero facilidad no equivale a buena arquitectura.

Los nuevos desarrolladores ganan mucho más aprendiendo:

  • asincronía
  • Promises
  • datos estructurados
  • IndexedDB

que intentando entender por qué "0" rompe una condición o por qué un usuario obtiene datos nulos después de una actualización.

Si hoy puedes evitar localStorage, hazlo.
And if you can't, at least use it knowing exactly what sacrifices you are making.

Preguntas frecuentes sobre localStorage en JavaScript (FAQ)

¿localStorage es seguro?

No para datos sensibles. Es vulnerable a XSS y accesible desde cualquier script.

¿Cuánto espacio permite localStorage?

Aproximadamente 5 MB, dependiendo del navegador, y sujeto a desalojo.

¿localStorage es síncrono o asíncrono?

Es completamente síncrono y bloquea el hilo principal.

¿IndexedDB reemplaza a localStorage?

Para la mayoría de aplicaciones modernas, sí debería hacerlo.

¿localStorage bloquea el main thread?

Sí. Cada operación de lectura y escritura.

Comparativa Técnica: localStorage vs. IndexedDB

 

CaracterísticalocalStorageIndexedDB
Tipo de DatosSolo Strings (UTF-16)Objetos, Blobs, Arrays, etc.
Capacidad~5 MB (rígido)% del disco (dinámico, mucho mayor)
Modo de OperaciónSíncrono (Bloquea UI)Asíncrono (No bloquea)
Seguridad (XSS)Muy vulnerableVulnerable (pero permite aislamiento)
Web WorkersNo disponibleCompatible
TransaccionesNoSí (Garantiza integridad)

Acepto recibir anuncios de interes sobre este Blog.

Descubre por qué esta API síncrona está dañando el rendimiento y la seguridad de tus aplicaciones web modernas. Analizamos sus riesgos frente a IndexedDB y cómo gestionar datos estructurados correctamente en JavaScript.

| 👤 Andrés Cruz

🇺🇸 In english