JavaScript Storage: localStorage, sessionStorage, and Cookies
Browser storage lets your JavaScript app remember data between page loads, tabs, or visits. This article explains the three main browser storage options—localStorage, sessionStorage, and cookies—so you can choose the right one for login state, user preferences, shopping carts, and other common tasks.
Quick answer: Use localStorage for long-lived client-side data, sessionStorage for data that should disappear when the tab closes, and cookies when the browser must send the data to the server with HTTP requests.
Difficulty: Beginner
You'll understand this better if you know: basic JavaScript variables, objects, JSON, and how the browser loads pages and makes requests.
1. What Is JavaScript Storage?
JavaScript storage is a set of browser features that let you keep small pieces of data on the client side. Unlike ordinary variables, stored data can survive a page refresh, and in some cases it can survive closing and reopening the browser.
- localStorage stores key-value pairs with no automatic expiration.
- sessionStorage stores key-value pairs for one tab or window session.
- Cookies store small key-value pairs that the browser can attach to HTTP requests.
- All three are useful, but they solve different problems.
These tools are not databases. They are simple storage mechanisms for small amounts of data, usually preferences, tokens, flags, or cached UI state.
2. Why JavaScript Storage Matters
Without browser storage, every refresh would reset your app state. Users would lose their theme choice, cart contents, draft filters, or login-related state unless the server re-sent everything each time.
Storage matters because it improves user experience and reduces repeated work. A small preference saved once can save many repeated clicks later.
It also helps you build offline-friendly or semi-persistent interfaces. For example, a form draft can survive a refresh, or a dashboard can reopen with the same filters the user selected earlier.
3. Basic Syntax or Core Idea
All three storage options are key-value based, but they do not use the same API style. localStorage and sessionStorage use the same Storage interface, while cookies are usually read and written through document.cookie.
Using localStorage
Store strings with setItem(), read them with getItem(), and remove them with removeItem().
const theme = "dark";
localStorage.setItem("theme", theme);
const savedTheme = localStorage.getItem("theme");
localStorage.removeItem("theme");The value is always stored as a string, so numbers and objects need conversion before saving and after reading.
Using sessionStorage
sessionStorage uses the same methods, but the data is scoped to the current tab and session.
sessionStorage.setItem("step", "2");
const currentStep = sessionStorage.getItem("step");This is useful for temporary state, such as a multi-step form that should not persist after the tab closes.
Using cookies
Cookies are set by assigning to document.cookie. Reading cookies gives you a single string that contains all accessible cookies for the current page.
document.cookie = "theme=dark; path=/; max-age=2592000";
const allCookies = document.cookie;Cookies are often used for server-readable session identifiers, but they require more care because they can affect every request to a matching path and domain.
4. Step-by-Step Examples
Example 1: Saving a theme preference with localStorage
A theme toggle is a classic use case for persistent browser storage. The user expects the choice to survive refreshes and future visits.
function saveTheme(theme) {
localStorage.setItem("theme", theme);
}
function loadTheme() {
return localStorage.getItem("theme") || "light";
}
saveTheme("dark");
const theme = loadTheme();This pattern is simple and effective because a small string is enough to represent the preference.
Example 2: Storing temporary form progress with sessionStorage
If the user should keep data only while the tab is open, sessionStorage is a better fit than localStorage.
const draft = {
name: "Ava",
email: "[email protected]"
};
sessionStorage.setItem("signupDraft", JSON.stringify(draft));
const savedDraft = JSON.parse(
sessionStorage.getItem("signupDraft") || "{}"
);This works well for drafts or onboarding flows that should reset after the session ends.
Example 3: Reading a cookie value in the browser
Sometimes you need to inspect a cookie value from JavaScript, such as a client-side preference cookie. The browser exposes accessible cookies as one string.
function getCookie(name) {
const parts = document.cookie.split("; ");
for (const part of parts) {
const [cookieName, cookieValue] = part.split("=");
if (cookieName === name) {
return decodeURIComponent(cookieValue);
}
}
return null;
}This shows why cookie parsing is less convenient than the localStorage API.
Example 4: Removing stored data
Cleaning up old storage is important when a preference is no longer valid or a user logs out.
localStorage.removeItem("theme");
sessionStorage.removeItem("signupDraft");
document.cookie = "theme=; path=/; max-age=0";The cookie line deletes the cookie by setting the same name and path with an expiration in the past.
5. Practical Use Cases
- Saving a dark mode or language preference with localStorage.
- Keeping a shopping cart in the browser for a simple storefront prototype.
- Remembering form progress with sessionStorage while the user completes a multi-step flow.
- Storing a non-sensitive server session token in a cookie when the backend needs it on every request.
- Saving feature flags or UI toggles that the app should load quickly before rendering.
As a rule, choose the smallest storage type that solves the problem and avoid storing data you do not need to persist.
6. Common Mistakes
Mistake 1: Treating storage values like numbers or objects
Storage APIs only store strings. If you save an object directly, you usually end up with [object Object], which is not useful.
Problem: The object is coerced into a string instead of being stored as structured data, so reading it back does not give you the original object.
const user = { name: "Mina", id: 42 };
localStorage.setItem("user", user);Fix: Use JSON.stringify() when saving and JSON.parse() when reading.
const user = { name: "Mina", id: 42 };
localStorage.setItem("user", JSON.stringify(user));
const savedUser = JSON.parse(localStorage.getItem("user") || "null");The corrected version works because JSON preserves the object structure.
Mistake 2: Using localStorage for tab-only state
Some data should disappear when the tab closes, such as an in-progress wizard or a temporary code entry. Saving that data in localStorage makes it persist longer than intended.
Problem: The data survives future visits, so old temporary state can confuse the user and reopen an outdated flow.
localStorage.setItem("checkoutStep", "3");Fix: Use sessionStorage when the data should be limited to the current tab session.
sessionStorage.setItem("checkoutStep", "3");The corrected version matches the lifetime of the data to the user experience.
Mistake 3: Assuming cookies are automatically secure
Cookies can be sent with requests, which is useful, but it also means they need careful settings. Leaving out attributes can expose them more broadly than intended.
Problem: A cookie without appropriate attributes may be available in more contexts than necessary, increasing the risk of misuse.
document.cookie = "sessionId=abc123";Fix: Add attributes such as path, max-age, SameSite, and Secure when appropriate for your app.
document.cookie = "sessionId=abc123; path=/; max-age=3600; SameSite=Lax; Secure";The corrected version is more explicit about how the cookie should behave in the browser.
7. Best Practices
Practice 1: Store only small, non-sensitive data
Browser storage is easy to access from client-side code, so it is not the right place for secrets. Keep passwords, private keys, and highly sensitive data off the client whenever possible.
// Better: store a preference, not a secret
localStorage.setItem("theme", "dark");This reduces risk and keeps storage fast and simple.
Practice 2: Wrap JSON parsing in a safe fallback
Reading storage can fail in practice if the value is missing or malformed. A fallback prevents the app from crashing on startup.
function loadSettings() {
try {
return JSON.parse(localStorage.getItem("settings") || "{}");
} catch {
return {};
}
}This pattern keeps bad stored data from breaking the page.
Practice 3: Use explicit names and cleanup rules
Clear key names help you understand what data is stored and when to remove it. Short, vague names make maintenance harder later.
localStorage.setItem("app:theme", "dark");
localStorage.setItem("app:lastRoute", "/dashboard");
function clearAppStorage() {
localStorage.removeItem("app:theme");
localStorage.removeItem("app:lastRoute");
}Consistent naming makes it easier to debug and delete data safely.
8. Limitations and Edge Cases
- localStorage and sessionStorage are synchronous, so heavy use can block the main thread.
- Storage size is limited. Exact quotas vary by browser and device, so large data should not go there.
- Cookies are sent with matching HTTP requests, which adds overhead on every request to that domain and path.
- Cookies have attributes that affect scope, expiration, and security, which can make them behave differently across environments.
- Private browsing modes may reduce quota or clear storage more aggressively.
- Storage is origin-scoped, so data saved on one protocol, domain, or port is not shared with another origin.
- Access can fail in some privacy settings or embedded contexts, so robust apps should handle missing storage gracefully.
If your app needs a large or structured client-side dataset, consider using IndexedDB instead of browser storage.
9. Practical Mini Project
Here is a small preference manager that loads a stored theme on page load, lets the user switch themes, and saves the choice in localStorage.
const themeSelect = document.querySelector("#themeSelect");
const status = document.querySelector("#status");
function applyTheme(theme) {
document.body.dataset.theme = theme;
themeSelect.value = theme;
status.textContent = `Theme saved: ${theme}`;
}
const storedTheme = localStorage.getItem("app:theme") || "light";
applyTheme(storedTheme);
themeSelect.addEventListener("change", event => {
const theme = event.target.value;
localStorage.setItem("app:theme", theme);
applyTheme(theme);
});This example shows the full loop: read stored data, apply it, respond to user input, and save the updated value.
10. Key Points
- localStorage persists until removed and is good for long-lived preferences.
- sessionStorage lasts for one tab session and is good for temporary state.
- Cookies are useful when the server must receive the data with requests.
- Storage values are strings, so objects usually need JSON serialization.
- Do not store secrets in client-side storage unless you fully understand the security tradeoffs.
11. Practice Exercise
- Build a small notes app that saves the selected theme in localStorage.
- Save a draft note in sessionStorage so it disappears when the tab closes.
- Add a cookie that remembers whether a user dismissed a banner for 24 hours.
Expected output: The theme should persist across refreshes, the draft should stay only for the current tab, and the banner dismissal should survive for one day.
Hint: Use JSON.stringify() for objects and keep each storage type tied to the lifetime you want.
const noteInput = document.querySelector("#noteInput");
const saveButton = document.querySelector("#saveButton");
function saveDraft() {
sessionStorage.setItem("draftNote", noteInput.value);
}
function loadDraft() {
noteInput.value = sessionStorage.getItem("draftNote") || "";
}
saveButton.addEventListener("click", saveDraft);
loadDraft();12. Final Summary
JavaScript browser storage gives you three main tools with different lifetimes and behaviors. localStorage is best for persistent client-side preferences, sessionStorage is best for tab-scoped temporary state, and cookies are best when the server needs the browser to send data automatically with requests.
The most important habit is matching the storage type to the job. If data should survive a refresh, use localStorage. If it should disappear when the tab closes, use sessionStorage. If the server must read it, use cookies and set the right attributes.
From here, the next useful topic is secure authentication storage and when to use cookies versus other browser-side persistence methods. That will help you make safer choices for login flows and user sessions.