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.
-
HTML Basics
- What is HTML: The Structure Layer of Every Web Page
- HTML Document Structure: DOCTYPE, html, head and body
- Elements, Tags and Attributes: The Vocabulary of HTML
- HTML Comments: Notes That Ship With Your Code
- Block Level and Inline Elements
- Writing and Running Your First HTML Page
- How a Browser Turns Markup Into a Page
- Text and Formatting
- Links and Navigation
- Images and Media
- Lists
- Tables
-
Forms
- Form Structure: form, action and method
- Input Types: Text, Email, Number, Date and the Rest
- Labels: The Most Important Element in a Form
- Checkboxes, Radio Buttons and Grouping
- select, option, optgroup and datalist
- textarea, File Uploads and Hidden Fields
- Buttons: submit, reset and button
- Built In Form Validation
- GET or POST: What Happens When a Form Is Submitted
- Semantic HTML
- HTML5 Features
- Head and Metadata
- HTML with CSS
- HTML with JavaScript
- Accessibility
-
HTML SEO
- How Google Works: Crawling, Indexing and Ranking
- SEO Friendly HTML Structure
- Titles and Descriptions That Earn Clicks
- Headings and Content Structure for Search
- Internal Linking and Anchor Text
- robots.txt and XML Sitemaps
- Canonical URLs and Duplicate Content
- Structured Data and JSON-LD
- Image SEO
- Core Web Vitals and Mobile Friendliness
- DevTools and Debugging
- Editor Productivity
- HTML Best Practices
- HTML Projects
- Advanced Projects
- Practice and Exams
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.
localStorage | sessionStorage | |
|---|---|---|
| Lifetime | Until explicitly cleared | Until the tab is closed |
| Shared across tabs | Yes, same origin | No, one tab only |
| Sent to the server | Never | Never |
| Typical limit | Around 5 MB per origin | Around 5 MB per origin |
| Accessible to script | Yes | Yes |
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 Storage | Cookies | |
|---|---|---|
| Sent with every request | No | Yes |
| Size | Around 5 MB | Around 4 KB |
| Server can read | No | Yes |
| Can be hidden from script | No | Yes, with HttpOnly |
| Expiry control | Manual | Built 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
accuracyvalue. - 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
| API | For | Needs permission |
|---|---|---|
| IndexedDB | Large structured client side data | No |
| Clipboard | Reading and writing the clipboard | Yes, for reading |
| Notifications | System notifications | Yes |
| Media Devices | Camera and microphone | Yes |
| Service Worker | Offline support and caching | No, but HTTPS only |
| Web Share | The native share sheet | No, 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.
setItemcan throw. Handle it.- The
storageevent 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.parseand 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
localStoragefor preferences andsessionStoragefor 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
HttpOnlycookies, never in script readable storage.
Practice
- Build a theme switch that persists in
localStorageand syncs across two open tabs. - Store an object, read it back without parsing, and explain what you get.
- Add a location button that explains itself first and degrades to a PIN code field on refusal.
- Fill
localStorageuntilsetItemthrows, and handle the failure gracefully.