Aller au contenu

localStorage : synchronisation entre onglets

Plusieurs onglets de la même origine partagent le même localStorage. L’événement storage prévient les autres onglets quand une valeur change.

window.addEventListener('storage', (evenement) => {
console.log(evenement.key); // clé modifiée (null si clear())
console.log(evenement.oldValue); // ancienne valeur (null si nouvelle clé)
console.log(evenement.newValue); // nouvelle valeur (null si supprimée)
console.log(evenement.url); // page qui a fait la modification
console.log(evenement.storageArea); // l'objet localStorage concerné
});

Règle essentielle : l’événement se déclenche dans les autres onglets et fenêtres de l’origine, pas dans celui qui a écrit. Un onglet doit donc mettre à jour son propre affichage directement, et réagir à storage pour les changements des autres.

function appliquerTheme(theme) {
document.documentElement.dataset.theme = theme;
}
function changerTheme(theme) {
appliquerTheme(theme); // onglet courant
try {
localStorage.setItem('theme', theme);
} catch {} // déclenche 'storage' ailleurs
}
window.addEventListener('storage', (e) => {
if (e.key === 'theme' && e.newValue) appliquerTheme(e.newValue);
});

Si l’utilisateur change le thème dans un onglet, les autres suivent immédiatement.

function deconnecter() {
localStorage.setItem('deconnexion', String(Date.now())); // signal
localStorage.removeItem('deconnexion');
allerALaPageDeConnexion();
}
window.addEventListener('storage', (e) => {
if (e.key === 'deconnexion' && e.newValue) allerALaPageDeConnexion();
});

Écrire une valeur différente à chaque fois : l’événement ne se déclenche que si la valeur change.

Situation Résultat
Écriture d’une valeur identique à l’actuelle Aucun événement
Écriture dans l’onglet courant Aucun événement dans cet onglet
Origines différentes (domaine, port, schéma) Stockages séparés, pas d’événement
Onglet en navigation privée et onglet normal Stockages séparés
sessionStorage Événement seulement entre iframe du même onglet

BroadcastChannel : un meilleur outil pour les messages

Section intitulée « BroadcastChannel : un meilleur outil pour les messages »

BroadcastChannel envoie des messages entre contextes de la même origine, sans passer par le stockage :

const canal = new BroadcastChannel('monapp');
canal.postMessage({ type: 'theme', valeur: 'dark' });
canal.onmessage = (e) => {
if (e.data.type === 'theme') appliquerTheme(e.data.valeur);
};
// à la fermeture
canal.close();
storage BroadcastChannel
Conserve la donnée Oui Non (message seulement)
Déclenché dans l’onglet émetteur Non Non
Données échangées Chaînes Objets (clonage structuré)
Usage Réagir à un changement de réglage mémorisé Signaux, notifications, coordination

Combiner les deux est courant : stocker la valeur dans localStorage (mémoire), la diffuser par BroadcastChannel (signal).

Deux onglets peuvent lire une valeur, la modifier et l’écrire presque en même temps : la dernière écriture écrase l’autre (perte de mise à jour).

// Risqué : lecture puis écriture non atomiques
const panier = lire('panier', []);
panier.push(article);
ecrire('panier', panier);

Solutions :

  • Relire juste avant d’écrire, et limiter la durée de la modification.
  • Écrire un élément par clé (panier:42) plutôt qu’une liste dans une seule clé.
  • Verrouiller avec l’API Web Locks :
await navigator.locks.request('panier', async () => {
const panier = lire('panier', []);
panier.push(article);
ecrire('panier', panier);
});

navigator.locks.request garantit qu’un seul onglet exécute le bloc à la fois pour un nom de verrou donné (disponible dans les navigateurs récents).

// Hook générique (React)
import { useSyncExternalStore } from 'react';
function souscrire(callback) {
window.addEventListener('storage', callback);
return () => window.removeEventListener('storage', callback);
}
export function useStockage(cle, defaut) {
return useSyncExternalStore(
souscrire,
() => localStorage.getItem(cle) ?? defaut,
() => defaut, // valeur côté serveur
);
}

Voir Avec Astro, React et Next.js.

Symptôme Piste
L’événement ne se déclenche jamais Onglet qui a écrit (déclenche ailleurs), valeur identique, autre origine
L’onglet courant ne se met pas à jour Mettre à jour directement l’affichage lors de l’écriture
Valeur obsolète après une écriture dans un autre onglet Écouter storage et relire
Perte de données avec deux onglets Écritures non atomiques : une clé par élément, ou navigator.locks
e.newValue est null Clé supprimée ou clear() appelé
BroadcastChannel sans réponse Noms de canaux différents, ou contexte d’origine différente

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

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

Aucune.