Web Storage and Browser APIs from a Page

localStorage, sessionStorage and geolocation. What a page can remember, what it must ask permission for, and what should never be stored in the browser.

Web Storage

Two key and value stores the browser keeps for a page, both scoped to the origin - the combination of scheme, host and port.

localStoragesessionStorage
LifetimeUntil explicitly clearedUntil the tab is closed
Shared across tabsYes, same originNo, one tab only
Sent to the serverNeverNever
Typical limitAround 5 MB per originAround 5 MB per origin
Accessible to scriptYesYes
localStorage.setItem("theme", "dark");
localStorage.getItem("theme");        // "dark"
localStorage.getItem("missing");      // null
localStorage.removeItem("theme");
localStorage.clear();
localStorage.length;
localStorage.key(0);

Everything is a string

localStorage.setItem("count", 5);
typeof localStorage.getItem("count");   // "string"

// objects need serialising both ways
localStorage.setItem("prefs", JSON.stringify({ theme: "dark", size: 16 }));
const prefs = JSON.parse(localStorage.getItem("prefs") || "{}");

It can throw

Two situations cause setItem to throw: the quota is exceeded, and private browsing modes that disable storage. Wrap writes.

function save(key, value) {
  try {
    localStorage.setItem(key, JSON.stringify(value));
    return true;
  } catch (error) {
    return false;      // quota exceeded or storage unavailable
  }
}

Reacting to changes in other tabs

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

The event fires in other tabs of the same origin, never in the tab that made the change. It is how a theme switch propagates across open tabs.

Storage versus cookies

Web StorageCookies
Sent with every requestNoYes
SizeAround 5 MBAround 4 KB
Server can readNoYes
Can be hidden from scriptNoYes, with HttpOnly
Expiry controlManualBuilt in

Which leads to the rule that matters most.

Never store a session token in Web Storage

Anything in localStorage is readable by any JavaScript running on the page, including a script injected through a cross site scripting flaw or pulled in from a compromised third party dependency. A stolen token is a stolen session.

Session credentials belong in a cookie marked HttpOnly, Secure and SameSite, which script cannot read at all.

Web Storage is for preferences and non sensitive state: theme, chosen language, a dismissed banner, a draft in progress, a recently viewed list.

Geolocation

Asks the reader for their position. It always requires explicit permission and only works over HTTPS.

function locate() {
  if (!("geolocation" in navigator)) {
    showMessage("Location is not available in this browser.");
    return;
  }

  navigator.geolocation.getCurrentPosition(
    (position) => {
      const { latitude, longitude, accuracy } = position.coords;
      showNearbyBranches(latitude, longitude, accuracy);
    },
    (error) => {
      const messages = {
        1: "Permission was refused.",
        2: "Position is unavailable.",
        3: "The request timed out.",
      };
      showMessage(messages[error.code] || "Could not determine location.");
    },
    { enableHighAccuracy: false, timeout: 10000, maximumAge: 300000 }
  );
}

// only ever from a real user action
document.getElementById("find-branch").addEventListener("click", locate);

Rules that make it usable

  • Only ask in response to an action. A permission prompt on page load is dismissed by most readers and, once refused, cannot be asked again.
  • Say why first. Explain what the location is for before the prompt appears.
  • Always handle refusal. Offer a manual alternative - a postcode field, a list of branches.
  • Do not assume accuracy. The reading may be a city block or a whole city; check the accuracy value.
  • HTTPS only. The API does not exist on an insecure origin.
<p>Find branches near you. We use your location once and do not store it.</p>
<button type="button" id="find-branch">Use my location</button>

<p>Or search by area:</p>
<label for="pin">PIN code</label>
<input type="text" id="pin" name="pin" inputmode="numeric" pattern="[0-9]{6}">

Other browser APIs worth knowing

APIForNeeds permission
IndexedDBLarge structured client side dataNo
ClipboardReading and writing the clipboardYes, for reading
NotificationsSystem notificationsYes
Media DevicesCamera and microphoneYes
Service WorkerOffline support and cachingNo, but HTTPS only
Web ShareThe native share sheetNo, user gesture required

Almost all of these require a secure context. Develop over localhost, which counts as secure, or over HTTPS.

Important rules

  • Storage is per origin. A different subdomain is a different store.
  • Values are strings; serialise anything else.
  • setItem can throw. Handle it.
  • The storage event fires in other tabs, not the current one.
  • Never store credentials, tokens or personal data client side.
  • Permission based APIs require a secure context and a user gesture.
  • A refused permission usually cannot be requested again without the reader changing a browser setting.

Common mistakes

  • Storing a session token in localStorage.
  • Forgetting JSON.parse and treating an object as a string.
  • Writing without a try block and breaking the page in private mode.
  • Requesting geolocation on page load.
  • No fallback when permission is refused.
  • Assuming storage persists forever; browsers may evict it under pressure.
  • Storing personal information in the browser without consent.

Best practices

  • Use localStorage for preferences and sessionStorage for per tab state.
  • Namespace keys with a prefix so they do not collide.
  • Wrap all reads and writes in a small helper with error handling.
  • Ask for permissions after explaining why, and only on an action.
  • Always offer a manual alternative to a permission based feature.
  • Keep credentials in HttpOnly cookies, never in script readable storage.

Practice

  1. Build a theme switch that persists in localStorage and syncs across two open tabs.
  2. Store an object, read it back without parsing, and explain what you get.
  3. Add a location button that explains itself first and degrades to a PIN code field on refusal.
  4. Fill localStorage until setItem throws, and handle the failure gracefully.

Useful resources

Hand picked references for this topic
Written by Lorens Mishra

Software Engineer Notes Management System Administrator

Continue reading

All HTML notes →
HTML

The canvas Element

A blank bitmap you draw on with JavaScript. Powerful for graphics and games, and invisible to every reader who cannot see the screen.

Read more
HTML

SVG in HTML

Vector graphics that scale perfectly, weigh almost nothing and can be styled with CSS. Learn inline SVG, SVG as an image, and how to make one accessib...

Read more
HTML

The template Element

Markup that is parsed but not rendered, waiting to be cloned. The clean way to build repeated content in JavaScript without writing HTML as strings.

Read more

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.