Aller au contenu

localStorage : limites et dépannage

Limite Détail
Taille Environ 5 Mo par origine (variable ; compté en caractères UTF-16)
Valeurs Chaînes seulement
Accès Synchrone : une grosse lecture ou écriture bloque l’interface
Contexte Pas disponible dans un Web Worker ni un service worker
Requêtes Aucune recherche : on lit une clé ou on parcourt tout
Transactions Aucune : pas d’écriture atomique de plusieurs clés
Origine Un stockage par origine, séparé entre http et https, entre sous-domaines et entre ports

Dépasser ces limites : IndexedDB.

try {
localStorage.setItem('gros', 'x'.repeat(6 * 1024 * 1024));
} catch (e) {
if (
e instanceof DOMException &&
(e.name === 'QuotaExceededError' || e.code === 22)
) {
console.warn('Stockage plein');
}
}

Conduite à tenir : libérer de la place (supprimer les anciennes données, un cache), prévenir l’utilisateur si une donnée importante n’a pas pu être enregistrée, et continuer à fonctionner sans le stockage.

Cause Conséquence
L’utilisateur efface les données du site Tout disparaît
Navigation privée Stockage vidé à la fermeture de la fenêtre (et parfois en lecture seule ou de capacité très réduite)
Réglage « effacer les cookies et données à la fermeture » Vidé à chaque fermeture
Manque d’espace disque Le navigateur peut évincer des données de sites peu utilisés
Protection contre le suivi de certains navigateurs Données de sites inutilisés pendant plusieurs jours supprimées

Ne jamais compter sur la présence d’une donnée : prévoir la reconstruction (valeur par défaut, nouvelle synchronisation avec le serveur).

L’API StorageManager permet de demander à ne pas être évincé et d’estimer l’espace :

if (navigator.storage?.persist) {
const accorde = await navigator.storage.persist();
console.log('Stockage durable :', accorde);
}
const { usage, quota } = await navigator.storage.estimate();
console.log(
`Utilisé : ${(usage / 1048576).toFixed(1)} Mo sur ${(quota / 1048576).toFixed(0)} Mo`,
);

Le navigateur décide d’accorder ou non la persistance (selon l’usage ou une interaction de l’utilisateur). estimate() concerne l’ensemble du stockage de l’origine (IndexedDB, caches…), pas seulement localStorage.

export function stockageDisponible() {
try {
const test = '__test__';
localStorage.setItem(test, test);
localStorage.removeItem(test);
return true;
} catch {
return false;
}
}

Utiliser cette fonction pour proposer un repli (mémoire, cookie) quand le stockage est indisponible.

const memoire = new Map();
export const stockage = stockageDisponible()
? localStorage
: {
getItem: (c) => memoire.get(c) ?? null,
setItem: (c, v) => void memoire.set(c, String(v)),
removeItem: (c) => void memoire.delete(c),
};
  • Les accès sont synchrones : éviter les écritures dans une boucle serrée ou à chaque frappe de touche.
  • Regrouper les écritures, ou les temporiser (anti-rebond) :
let minuteur;
function sauvegarderBrouillon(texte) {
clearTimeout(minuteur);
minuteur = setTimeout(() => {
try {
localStorage.setItem('brouillon', texte);
} catch {}
}, 500);
}
  • Ne pas stocker de très grosses chaînes (images en base64, longues listes) : chaque lecture parse tout.
  • Lire une fois au démarrage, garder la valeur en mémoire, écrire à la modification.

Outils de développement (F12) :

Navigateur Où
Firefox Onglet Stockage > Stockage local
Chrome, Chromium, Edge Onglet Application > Stockage local

On peut y voir, modifier et supprimer les entrées, et vider le stockage du site (« Clear site data »).

// Lister avec les tailles, en console
console.table(
Object.entries(localStorage).map(([k, v]) => ({
cle: k,
caracteres: v.length,
})),
);

Surveiller les écritures pendant le débogage :

const original = Storage.prototype.setItem;
Storage.prototype.setItem = function (cle, valeur) {
console.log('setItem', cle, String(valeur).slice(0, 80));
return original.call(this, cle, valeur);
};

À retirer après le diagnostic.

Symptôme Piste
QuotaExceededError Stockage plein ou navigation privée : nettoyer, réduire, passer à IndexedDB
Les données ont disparu Effacement par l’utilisateur, navigation privée, réglage du navigateur
null alors que la valeur a été écrite Autre origine (http/https, www, port), autre profil, autre navigateur
SecurityError à l’accès Données de sites bloquées, iframe avec sandbox, file:// selon le navigateur
Interface qui se fige Écritures volumineuses et fréquentes : temporiser, réduire la taille
Deux onglets qui s’écrasent Écritures non atomiques : clé par élément, navigator.locks
Le stockage de localhost mélangé avec d’autres projets Même origine pour tous les projets sur le même port : préfixer les clés
Ancienne donnée qui fait planter la nouvelle version Numéro de version et migration (voir JSON et structure des données)

Ubuntu 24.04 et 26.04 : différences à connaître

Section intitulée « Ubuntu 24.04 et 26.04 : différences à connaître »

Aucune propre à Ubuntu. Le navigateur par défaut (Firefox, en paquet snap) et Chromium partagent les mêmes limites ; la capacité exacte varie selon le navigateur et sa version.