almessadi.
Back to Index

Use `localStorage` for Preferences, IndexedDB for Data_

The right browser storage choice depends on workload. `localStorage` is fine for tiny synchronous state; IndexedDB is the better fit for offline data and larger caches.

PublishedMay 12, 2024
Reading Time6 min read

localStorage is not evil. It is just easy to misuse.

It works well for:

  • a theme preference
  • a small feature flag cache
  • lightweight session hints

It is a poor fit for:

  • large offline datasets
  • draft systems with lots of content
  • queues of sync operations
  • image or binary payloads

The Main Limitation

The API is synchronous.

That means reads and writes happen on the main thread, which is exactly where you do not want large storage work happening in a modern app.

For small keys, that is usually fine. For application-sized caches, it becomes a design smell.

When IndexedDB Is the Better Choice

IndexedDB is a much better fit when you need:

  • structured records
  • asynchronous access
  • transactions
  • larger client-side persistence

That is why serious offline-capable web apps usually end up there eventually.

The downside is ergonomics. The native API is not pleasant to use directly.

Why Libraries Help

Wrappers like Dexie make IndexedDB much easier to treat as part of the application architecture rather than a low-level browser oddity.

import Dexie, { type EntityTable } from "dexie";

interface Draft {
  id: number;
  title: string;
  body: string;
}

const db = new Dexie("writer") as Dexie & {
  drafts: EntityTable<Draft, "id">;
};

db.version(1).stores({
  drafts: "++id, title",
});

That is often enough to make browser-side persistence feel maintainable.

A Better Rule of Thumb

Use:

  • localStorage for tiny preference-like state
  • IndexedDB for real application data

That line will save a lot of frontend architecture pain.

Further Reading