localStorage event listener & storage event: addEventListener('storage') (local storage event listener)

Last reviewed: by
localStorage event listener & storage event: addEventListener('storage') (local storage event listener)

People look up the same behavior under many names: localstorage event listener, local storage event listener, or storage event listener. Shorter queries like localstorage listener, localstorage events, and localstorage event point at the same API surface. The phrase localstorage change event describes what you care about—another document touched localStorage—while addeventlistener storage and window.addEventListener('storage', …) are the wiring. A common MDN-style question is why the storage event is not fired on the same window that called setItem; that is by design, not a bug.

This page explains native cross-tab delivery, the StorageEvent fields, and an optional synthetic dispatchEvent pattern for same-tab code reuse. For listener basics, see JavaScript addEventListener.

Verified with Node.js v20.18.2: All code samples and commands in this article were executed successfully (happy-dom 15.11.7 for the storage event checks).


Native rule: not fired on the same window

Per the Window: storage event documentation, a document receives the event when another same-origin document mutates the shared localStorage area. The browsing context that performed setItem, removeItem, or clear does not receive its own native storage event.

Diagram: Tab A writes localStorage while Tab B on the same origin receives the storage event; the writing tab does not

The script below registers a storage listener, writes to localStorage, and counts deliveries. In happy-dom (and real browsers), the count after setItem stays zero:

javascript
let fires = 0;
window.addEventListener("storage", () => {
  fires++;
});
localStorage.setItem("loggedIn", "true");
console.log(String(fires));
text
0

Other tabs on the same origin do get the event with populated key / oldValue / newValue (subject to browser timing). Each happy-dom Window has isolated storage in typical automated setups, so a single test window cannot emulate two real tabs; it only proves the same-window rule above.

For sessionStorage, MDN limits delivery to other browsing contexts in the same tab (for example iframes), not arbitrary other tabs; see the same storage event page.


window.addEventListener("storage", handler)

Register once per page. The handler receives a StorageEvent describing what changed elsewhere.

javascript
window.addEventListener("storage", (event) => {
  if (event.key === "theme") {
    applyTheme(event.newValue);
  }
});

function applyTheme(theme) {
  document.body.className = theme ?? "";
}

Useful fields on event (StorageEvent):

  • key — which entry changed (null when clear() wiped the whole object).
  • oldValue / newValue — previous and new string values (or null when adding/removing).
  • url — document URL reported for the initiating context.
  • storageArea — the Storage object (localStorage or sessionStorage) that changed.

Same-tab reuse: synthetic StorageEvent

If you want one storage handler to run in the tab that performed the write, the platform will not do that for you natively. Options:

  1. Call the same function you would call from the listener immediately after setItem.
  2. Or dispatch a synthetic StorageEvent yourself (still not a substitute for real cross-tab propagation—it only affects this window).

Listener receiving an explicit event:

javascript
const lines = [];
window.addEventListener("storage", (event) => {
  lines.push(`${event.key}:${String(event.oldValue)}->${String(event.newValue)}`);
});
window.dispatchEvent(
  new StorageEvent("storage", {
    key: "theme",
    oldValue: "light",
    newValue: "dark",
    url: window.location.href,
    storageArea: localStorage
  })
);
console.log(lines.join(";"));
text
theme:light->dark

Helper that writes storage then notifies local listeners (pattern only—keep your real applyTheme / state logic in one place):

javascript
const heard = [];
window.addEventListener("storage", (e) => {
  heard.push(`${e.key}=${e.newValue}`);
});

function updateLocalStorage(key, newValue) {
  const oldValue = localStorage.getItem(key);
  localStorage.setItem(key, newValue);
  window.dispatchEvent(
    new StorageEvent("storage", {
      key,
      oldValue,
      newValue,
      url: window.location.href,
      storageArea: localStorage
    })
  );
}

updateLocalStorage("theme", "dark");
updateLocalStorage("theme", "light");
console.log(heard.join("|"));
text
theme=dark|theme=light

For cross-tab UX (theme, auth, cart JSON), rely on the native event in every other tab, and in the writer tab call your updater directly so UI stays in sync without pretending the browser fired storage.

Diagram: storage events fire on storage mutations versus BroadcastChannel for explicit cross-context messages


Summary

If you are wiring a localstorage event listener or searching for local storage event listener examples, remember the platform rule first: the native storage event is for other same-origin documents, not for the tab that just called setItem. That is why people ask whether localstorage change event fired “here” after their own write—it will not, by design, and MDN-style questions about the event not firing on the same window are answered by that contract. Use window.addEventListener("storage", handler) (or addEventListener on window) and read key, oldValue, newValue, url, and storageArea from the StorageEvent.

When you still want one handler to run in the writer tab, the usual FAQ is whether to fake it: either call the same UI function immediately after mutating storage, or dispatch a synthetic StorageEvent for local listeners, understanding that it is not a substitute for real cross-tab synchronization. Pair this with sensible HTTP caching when you are trying to force fresh data instead of reaching for deprecated reload flags.


References

Olorunfemi Akinlua

Boasting over five years of experience in JavaScript, specializing in technical content writing and UX design. With a keen focus on programming languages, he crafts compelling content and designs user-friendly interfaces to enhance digital …

  • JavaScript
  • Web Design