JavaScript localStorage: Saving Data in the Browser

localStorage lets your page remember data between visits — no server required. Learn how to store, read, and remove values, why everything is a string, and the gotchas around quotas, JSON, and security.

Published August 13, 202610 min readBy ACY Partner Indonesia
JavaScript localStorage — saving key-value data in the browser
300 × 250Ad Space AvailablePlace your ad here

Refresh a page and, by default, everything your JavaScript was holding disappears. The variables, the user’s choices, the half-filled form — all gone, because that memory only lives as long as the page is open. For a lot of features that’s a problem. A “dark mode” toggle that resets on every reload is useless. A shopping cart that empties when someone clicks a link is worse. You need a way to make the browser remember something between visits, and the simplest tool for that job is localStorage.

localStorage is a small key-value store built into every modern browser. You hand it a name and a value, it tucks them away on the user’s machine, and they survive page reloads, tab closes, and even restarting the computer. No server, no database, no setup — just a few method calls. In this article you’ll learn how to store and read data, why every value comes back as a string, how to store objects and arrays properly, and the limits and security points that matter in real code.

What localStorage is

localStorage is a property on the global window object, so it’s available everywhere in browser JavaScript. It stores data as key-value pairs, where both the key and the value are strings. Think of it as a tiny notebook the browser keeps for your site: you write a labeled note, and it stays there until you erase it.

Two things make it genuinely useful:

  • It persists. Data written to localStorage stays until your code deletes it or the user clears their browser data. Closing the tab or shutting down the computer doesn’t wipe it.
  • It’s per-origin. Each site (more precisely, each origin — the combination of protocol, domain, and port) gets its own private storage. Another website can’t read what your site saved, and vice versa.

There’s a sibling called sessionStorage with an identical API. The only difference is lifespan: sessionStorage is cleared the moment the tab is closed. We’ll come back to it near the end — for now, everything we cover applies to both.

The four core methods

The whole API is small enough to learn in one sitting. There are really just four methods you’ll use:

localStorage.setItem("theme", "dark");   // save a value
localStorage.getItem("theme");           // read it back  → "dark"
localStorage.removeItem("theme");        // delete one key
localStorage.clear();                    // wipe everything for this origin

Let’s walk through each one.

Saving a value with setItem

setItem takes two arguments — a key and a value — and stores them. If the key already exists, its value is overwritten:

localStorage.setItem("username", "John Doe");
localStorage.setItem("language", "en");

That’s the entire operation. The data is now on disk, and it’ll still be there when the user comes back tomorrow.

Reading a value with getItem

getItem takes a key and returns its stored value:

const name = localStorage.getItem("username");
console.log(name);   // "John Doe"

The one detail worth burning into memory: if the key doesn’t exist, getItem returns null — not undefined, not an empty string. That null is your signal that the value was never set, and you’ll often check for it before using the result:

const theme = localStorage.getItem("theme");

if (theme === null) {
  // nothing saved yet — fall back to a default
  applyTheme("light");
} else {
  applyTheme(theme);
}

Removing data

removeItem deletes a single key, leaving everything else untouched. clear wipes the entire store for your origin:

localStorage.removeItem("username");   // just this one key
localStorage.clear();                  // every key your site saved

Reach for clear carefully. It removes everything your site has stored, which is usually what you want on a “log out” or “reset” action, but rarely what you want otherwise.

Inspect localStorage in DevTools

You don’t have to guess what’s in storage. Open your browser’s developer tools, go to the Application tab (it’s Storage in Firefox), and you’ll find Local Storage listed by origin. You can see every key and value, edit them by hand, and delete them — which is invaluable while debugging. If a value isn’t what you expect, this is the first place to look.

Everything is a string

Here’s the single most important thing to understand about localStorage, and the source of most beginner bugs: it can only store strings. Both keys and values are stored as text. If you try to save anything else, the browser quietly converts it to a string first — and that conversion is rarely what you want.

Watch what happens with a number:

localStorage.setItem("score", 42);

const score = localStorage.getItem("score");
console.log(score);          // "42"  ← a string, not the number 42
console.log(score + 1);      // "421" ← string concatenation, not 43!
console.log(typeof score);   // "string"

The 42 went in as a number but came back as the string "42". When you then did score + 1, JavaScript glued the strings together instead of adding. If you need a number back, convert it explicitly:

const score = Number(localStorage.getItem("score"));
console.log(score + 1);   // 43  ✓

The same trap snaps shut even harder on objects and arrays. Storing one directly is a classic mistake:

const user = { name: "Jane Doe", age: 28 };
localStorage.setItem("user", user);

console.log(localStorage.getItem("user"));   // "[object Object]"  😱

That "[object Object]" is JavaScript’s default way of turning an object into a string, and it’s completely useless — every field is gone. So how do you store an object? You convert it to JSON yourself first.

Storing objects and arrays with JSON

Since localStorage only speaks strings, you turn structured data into a string on the way in, and parse it back into an object on the way out. The tools for that are JSON.stringify and JSON.parse, which we covered in the JSON article. This pattern is the backbone of nearly every real localStorage use:

const user = { name: "Jane Doe", age: 28, isMember: true };

// SAVE: object → JSON string → localStorage
localStorage.setItem("user", JSON.stringify(user));

// READ: localStorage → JSON string → object
const saved = JSON.parse(localStorage.getItem("user"));

console.log(saved.name);   // "Jane Doe"
console.log(saved.age);    // 28  ← a real number again

JSON.stringify(user) turns the object into the text '{"name":"Jane Doe","age":28,"isMember":true}', which localStorage is happy to hold. On the way back, JSON.parse rebuilds the actual object — with its numbers as numbers and booleans as booleans — so saved.age is the number 28, not a string. Arrays work exactly the same way:

const cart = ["Keyboard", "Mouse", "Monitor"];
localStorage.setItem("cart", JSON.stringify(cart));

const items = JSON.parse(localStorage.getItem("cart"));
console.log(items.length);   // 3

Parsing null and bad data throws

There are two ways this pattern blows up. First, if the key was never set, getItem returns null, and JSON.parse(null) returns null rather than throwing — but JSON.parse of an invalid string (corrupted data, a value that isn’t JSON) throws a SyntaxError that will crash your script if it’s not caught. Always guard reads that involve parsing:

function loadUser() {
  const raw = localStorage.getItem("user");
  if (raw === null) return null;          // nothing stored
  try {
    return JSON.parse(raw);
  } catch {
    return null;                          // stored value was corrupt
  }
}

That little wrapper keeps a single bad value in storage from taking down your whole page. If you’re shaky on try/catch, the error handling article walks through it.

A practical example: a saved theme toggle

Let’s tie it together with something you’d actually ship — a dark/light theme that the browser remembers. This is one of the most common real uses of localStorage:

const toggleButton = document.querySelector("#theme-toggle");

// On page load: apply whatever was saved last time
const savedTheme = localStorage.getItem("theme") || "light";
document.body.dataset.theme = savedTheme;

// When the user clicks: flip the theme and remember the choice
toggleButton.addEventListener("click", () => {
  const current = document.body.dataset.theme;
  const next = current === "light" ? "dark" : "light";

  document.body.dataset.theme = next;
  localStorage.setItem("theme", next);   // persist it
});

The flow is the whole point. On load, you read the saved theme (falling back to "light" if nothing’s there yet, thanks to the || "light"). On every click, you flip the value and write it back. Because localStorage persists, the next visit reads the saved choice and applies it before the user even touches the page. If the DOM and event code here looks unfamiliar, the events article covers addEventListener.

Limits, security, and when not to use it

localStorage is wonderfully simple, but it isn’t a database, and treating it like one leads to trouble. A few honest limits:

It’s small. Browsers cap localStorage at roughly 5 MB per origin. That’s plenty for preferences, small caches, and draft text, but not for images, large datasets, or anything heavy. Exceed the quota and setItem throws a QuotaExceededError:

try {
  localStorage.setItem("big", hugeString);
} catch (err) {
  if (err.name === "QuotaExceededError") {
    console.warn("Storage is full — couldn't save.");
  }
}

It’s synchronous. Every read and write blocks the main thread until it finishes. For the tiny values localStorage is meant for, that’s imperceptible. But writing megabytes of JSON on every keystroke will make your UI stutter, which is another reason to keep stored values small.

It’s strings only, as you’ve now seen, which is why JSON is your constant companion here.

Never store sensitive data

This is the rule that matters most. localStorage is not secure. Any JavaScript running on your page — including a malicious script injected through a cross-site scripting (XSS) vulnerability — can read everything in it instantly. It has no encryption and no access controls. So never store passwords, credit card numbers, authentication tokens, or any personal data you’d hate to see leaked. Treat localStorage as a public notepad: fine for a theme preference or a draft, completely wrong for secrets.

So when should you use it? localStorage shines for low-stakes, non-sensitive data that improves the experience but isn’t critical: UI preferences (theme, language, layout), a “you’ve seen this banner” flag, a draft a user is typing, or a small cache to avoid re-fetching data they just loaded. If the data is sensitive, large, or absolutely must not be lost, it belongs on a server instead.

localStorage vs sessionStorage vs cookies

It helps to know how localStorage sits next to its neighbors:

  • localStorage — persists until explicitly cleared. Survives reloads, tab closes, and reboots. Your default choice for browser-side persistence.
  • sessionStorage — identical API, but cleared when the tab closes. Use it for data that should only live for one session, like a multi-step form in progress that shouldn’t carry over to a brand-new tab.
  • Cookies — older, smaller (around 4 KB), and sent to the server with every HTTP request. That automatic transmission makes them right for things the server needs (like session IDs), but wasteful for purely client-side data. For data only your JavaScript reads, localStorage is simpler and roomier.

A quick way to choose: need it client-side and persistent? localStorage. Client-side but only for this tab? sessionStorage. Does the server need to read it on every request? Cookie.

Wrapping up

localStorage gives your pages a memory, and the whole API fits in your head:

  • It’s a per-origin, persistent key-value store on window, available everywhere in browser JavaScript. Data survives reloads, tab closes, and reboots until your code or the user clears it.
  • Four methods do everything: setItem(key, value) to save, getItem(key) to read (returns null if the key is missing), removeItem(key) to delete one key, and clear() to wipe your origin’s storage.
  • Everything is a string. Numbers come back as text, so convert with Number(). Store objects and arrays with JSON.stringify going in and JSON.parse coming out — and wrap the parse in try/catch.
  • Respect the limits: roughly 5 MB, synchronous, and never for sensitive data — anything on the page can read it.
  • Reach for sessionStorage when data should die with the tab, and cookies when the server needs the value on each request.

With a few setItem and getItem calls, you can build features that feel polished — a theme that sticks, a form that doesn’t lose your work, a setting the browser quietly remembers. It’s one of the smallest APIs in JavaScript and one of the most immediately satisfying to use.

Tags:javascriptfrontendlocalstorageweb storageintermediate
728 × 90Ad Space AvailablePlace your ad here

Related Articles

See All Articles

You Might Also Like

Frontend best practices overview cover
Frontend / Fundamentals

Frontend Best Practices: An Overview

A friendly map of what good frontend really means — semantic HTML, clean CSS, unobtrusive JavaScript, responsive layouts, performance, and progressive enhancement, with pointers to go deeper.

Sep 20, 20268 min read
A Frontend Developer Roadmap cover with the path HTML to CSS to JavaScript
Frontend / Fundamentals

A Frontend Developer Roadmap: What to Learn, and in What Order

A calm, step-by-step learning path for beginners: HTML, CSS, JavaScript, developer tools, responsive and accessible design, a framework, TypeScript, and deployment, with the reasoning behind each step.

Sep 20, 202611 min read
Title card reading Styling and Interactivity over a dark blue ACY Partner background
Frontend / Fundamentals

How Styling and Interactivity Work

A beginner-friendly look at how CSS turns plain HTML into a designed layout, and how JavaScript adds behavior — the structure, style, behavior mental model for building web pages.

Sep 20, 20269 min read
Three front-end layers combining on one web page
Frontend / Fundamentals

How HTML, CSS, and JavaScript Work Together

A hands-on walkthrough for beginners: build one small button, give it structure with HTML, looks with CSS, and behavior with JavaScript, and watch the three layers cooperate on a real page.

Sep 20, 20268 min read
The Frontend Developer Toolkit cover with editor, browser, and CLI motifs
Frontend / Fundamentals

The Frontend Developer Toolkit

A friendly tour of the everyday tools a frontend developer relies on — code editor, browser and DevTools, terminal, version control, package managers, and a dev server — and what each one is actually for.

Sep 20, 202610 min read
Cover illustration for What Frontend Developers Do
Frontend / Fundamentals

What Frontend Developers Do: A Beginner's Guide to the Role

A clear, beginner-friendly look at what frontend developers actually do every day: turning designs into working interfaces, building reusable UI, and caring about responsiveness, accessibility, and speed.

Sep 20, 202610 min read