Every website you visit needs to remember things. Your login session, the dark-mode toggle you flipped, the items sitting in your shopping cart, a draft message you started typing — all of that has to live somewhere on your device, because the page itself disappears the moment you close the tab. The browser gives websites several different drawers to keep this data in, and each drawer has its own size, its own rules about how long things stay, and its own quirks.
If you are new to web development, this can feel confusing. People throw around words like cookie, localStorage, and IndexedDB as if everyone already knows the difference. By the end of this article, you will know exactly what each one is, how they compare, and which one to pick for a given job. We will keep things conceptual and plain — no deep code required.
Why the browser needs storage at all
The web works in short bursts. Your browser asks a server for a page, the server sends it back, and then the connection is essentially done. The server does not “remember” you between those requests on its own — each request starts fresh, almost like meeting a stranger again and again.
To fix this, the browser keeps small pieces of information on your computer and hands them back when needed. Think of it like a coat-check at a theatre: you hand over your coat, you get a ticket, and later that ticket proves the coat is yours. Browser storage is that coat-check. It lets a site recognise you, restore your settings, and avoid asking you the same questions every single time.
There are four main options you will meet, and they fall into two camps: storage the server gets to see (cookies) and storage that stays purely on the device (everything else).
Cookies: small notes that travel with every request
A cookie is a tiny piece of text — a name and a value — that the browser stores and then automatically attaches to every request it sends to that same website. That last part is the key to understanding cookies: they travel.
Here is the flow in plain terms:
Browser Server
| ---- request a page ------> |
| <--- "Set-Cookie: id=abc" -- | (server asks browser to store a cookie)
| |
| ---- request again --------> |
| "Cookie: id=abc" | (browser sends it back, every time)
Because cookies ride along with each request, they are the natural home for things the server needs to know on every visit — most importantly, your login session. When you sign in, the server typically hands your browser a session cookie, and from then on the browser quietly proves who you are by sending that cookie with each request.
Cookies are small. The practical limit is roughly 4 kilobytes per cookie — enough for an ID or a short token, not for storing a list of products or an image. You can also control how long a cookie lasts and how it behaves with flags like Expires, Secure, and HttpOnly.
Cookies are sent on every request
Because the browser attaches cookies to each request to that site, storing large data in cookies makes every request heavier and slower. Keep cookies tiny, and never put big blobs of data in them.
A quick note on the privacy reputation cookies carry: the cookies that track you across many sites are third-party cookies, set by domains other than the one in your address bar. The plain first-party cookie that keeps you logged in to one site is a normal, useful tool — not the villain.
localStorage: a simple drawer that stays put
localStorage is a key-value store built into the browser. You give it a name and a string value, and it keeps them — even after you close the tab, close the browser, or restart your computer. The data sits there until your code deletes it or the user clears their browsing data.
The interface is refreshingly simple. You store, you read, you remove:
// Save a value
localStorage.setItem("theme", "dark");
// Read it back later (even after a restart)
const theme = localStorage.getItem("theme"); // "dark"
// Remove it
localStorage.removeItem("theme");
Two things set localStorage apart from cookies. First, it is much bigger — browsers typically allow around 5 megabytes per site, which is plenty for preferences, cached settings, or a saved draft. Second, and importantly, it is never sent to the server. It lives entirely in the browser, so it does not slow down your requests.
That makes localStorage perfect for client-side preferences: the theme a visitor chose, which panels they collapsed, whether they dismissed a banner. The catch is that it only stores plain text strings, so structured data has to be converted to and from JSON.
localStorage only stores strings
To save an object or array, convert it with JSON.stringify() before storing, and JSON.parse() when you read it back. Forgetting this is one of the most common beginner stumbles.
There is one safety rule worth burning into memory: never store anything sensitive in localStorage. Any script running on the page can read it, so passwords, tokens, or personal data do not belong there.
sessionStorage: the same idea, but just for this tab
sessionStorage works exactly like localStorage — same methods, same string-only values — with one crucial difference: it is tied to a single tab, and it disappears the moment that tab is closed.
Open blog.acy-partner.com in two separate tabs and each tab gets its own private sessionStorage; they do not share anything. Close one tab and its data is gone. Reopen the site and you start fresh.
This makes sessionStorage ideal for short-lived, throwaway state that should not stick around: the current step of a multi-step form, a scroll position you want to restore on a back-button press within the same visit, or a flag that a popup has already shown during this session. If it should be forgotten when the user leaves, sessionStorage is the right drawer.
// Lives only in this tab, only until it closes
sessionStorage.setItem("wizardStep", "3");
const step = sessionStorage.getItem("wizardStep"); // "3"
IndexedDB: a real database inside the browser
The first three options are great for small, simple values. But what if you need to store a lot of structured data — say, hundreds of records so a web app keeps working offline? That is what IndexedDB is for. It is a full database that lives inside the browser.
Unlike localStorage, IndexedDB can hold large amounts of data (often hundreds of megabytes or more, depending on the device and available disk space). It stores structured objects rather than plain strings, and it lets you search and index them, much like a database on a server would.
The trade-off is complexity. IndexedDB is more powerful but also harder to use directly — its raw interface is verbose and works through events and asynchronous requests. In real projects, most developers reach for a small helper library that wraps IndexedDB in a friendlier set of commands, rather than calling it by hand.
You reach for IndexedDB when the data is too big or too structured for the simpler options: an offline email client caching messages, a note-taking app storing documents, or a map app keeping tiles for an area you downloaded.
Most apps never need IndexedDB
If you only need to remember a theme or a login state, cookies and localStorage are enough. Reach for IndexedDB only when you genuinely need to store large, searchable, structured data on the device.
Putting them side by side
Here is the whole picture in one place. When you are deciding where to keep a piece of data, walk down this table and pick the first row that fits.
| Feature | Cookies | localStorage | sessionStorage | IndexedDB |
|---|---|---|---|---|
| Typical size limit | ~4 KB | ~5 MB | ~5 MB | Hundreds of MB+ |
| How long it lasts | Until expiry you set | Until cleared | Until tab closes | Until cleared |
| Scope | The whole site | The whole site | One tab only | The whole site |
| Sent to the server? | Yes, every request | No | No | No |
| Data type | Short text | Strings only | Strings only | Structured objects |
| Good for | Sessions, server-read flags | Preferences, small data | Per-tab temporary state | Large offline datasets |
Choosing the right one in practice
Most decisions come down to a couple of plain questions.
First: does the server need to read this on every request? If yes — a login session is the classic case — use a cookie, and keep it small. If the data only matters in the browser, skip cookies entirely so you do not slow down every request.
Second: how long should it stick around? If it must survive restarts (a saved preference), reach for localStorage. If it should vanish when the tab closes (a half-finished form), use sessionStorage.
Third: how much data, and how structured? If it is a handful of small values, localStorage is plenty. If it is a large, searchable collection — the kind of thing that powers offline apps — that is where IndexedDB earns its keep.
Imagine a small online store run by ACY Partner Indonesia. A shopper named Jane Doe logs in: that session lives in a cookie so the server recognises her. Her chosen language and dark-mode setting go in localStorage so they survive between visits. The current step of her checkout wizard sits in sessionStorage so it resets if she abandons the tab. And if the store offered an offline product catalogue, those thousands of items would live in IndexedDB. Four kinds of data, four drawers, each chosen on purpose.
Wrapping up
Browser storage is not one thing — it is a small toolbox, and the skill is matching the tool to the job. Cookies are tiny notes that travel to the server on every request, ideal for sessions. localStorage is a roomy drawer that persists and stays on the device, perfect for preferences. sessionStorage is the same idea scoped to a single tab and wiped when it closes. And IndexedDB is a real in-browser database for large, structured, searchable data.
Keep two rules in mind above all: never put sensitive data in storage that any script can read, and never bloat your cookies with data the server does not actually need. Get those right and you will sidestep the most common mistakes.
From here, a natural next step is to look more closely at how cookie flags like Secure, HttpOnly, and SameSite protect a login session, and how the same-origin rules decide which pages are even allowed to read a given store. Those details build directly on the foundation you just laid down.