Lesson overview · Free interview practice

Browser Storage

Learn browser storage mechanisms and best practices.

Topics in the full lesson
  • Introduction to Browser Storage
  • Web Storage API
  • Using localStorage
  • Using sessionStorage
  • IndexedDB
  • Overview of Web SQL
  • Comparing Storage Mechanisms
  • Best Practices
  • Exercises

Practice Problems

Work through a question before revealing its explanation. These questions and answers are free; Premium adds the full lesson walkthrough, examples and implementation detail.

Completion marks record your own progress, not an automatically checked result. The task type does not determine whether it is optional.

TheoryEasy

Client-Side Storage Security: What Belongs Where

Question

What are the security considerations when choosing client-side storage? Why do experienced engineers never put authentication tokens in localStorage?

Take a moment to think about this before revealing the answer

Explain your reasoning or try an implementation before comparing answers.

TheoryEasy

`localStorage` vs `sessionStorage`

Question

What is the difference between localStorage and sessionStorage? Show the API and the practical implications of each.

Take a moment to think about this before revealing the answer

Explain your reasoning or try an implementation before comparing answers.

TheoryEasy

When to Use IndexedDB Instead of `localStorage`

Question

When do you reach for IndexedDB over localStorage? What does IndexedDB give you that Web Storage doesn't?

Take a moment to think about this before revealing the answer

Explain your reasoning or try an implementation before comparing answers.

TheoryMedium

Cookies as a Storage Mechanism: `HttpOnly`, `Secure`, `SameSite`

Question

When should you store data in a cookie instead of localStorage? Walk through the security attributes (HttpOnly, Secure, SameSite) that make cookies safer than Web Storage for tokens.

Take a moment to think about this before revealing the answer

Explain your reasoning or try an implementation before comparing answers.

CodingHard

Implement a Small IndexedDB Promise Wrapper

Implement and check your solution in the stated environment before revealing the answer.

Question

Implement idbStore({ db, store, version = 1 }), returning { get(key), set(key, value), delete(key) } as Promise-based operations. Explain why adapting the native event API this way is useful.

Before you start

This is an advanced extension of the native IndexedDB examples. You need Promise construction, IDBOpenDBRequest, upgrade events, request events, transaction complete and abort, and structured-clone-compatible data. Do not substitute a fixed delay for database readiness.

Use a real browser on an isolated origin. The companion page and checks use native IndexedDB, not a Node database mock. Start the provided server with Node 24.21.0:

node fixtures/server.mjs

Open this page in a fresh browser profile or context:

http://127.0.0.1:3056/idb/?candidate=starter

The server also reserves 127.0.0.1:3055 for the service-worker exercise. Do not reuse occupied ports or a shared application origin. No credentials, external service or real user data are needed.

Input and result contract

  • db and store are nonempty strings. Invalid names throw TypeError when constructing the wrapper. version is a positive safe integer, otherwise construction throws RangeError.
  • Open lazily on first use. Concurrent calls share the same pending open or cached connection.
  • During a version upgrade, create the named object store if absent, with out-of-line keys and no auto-increment. Do not invent schema migration on a same-version open. A same-version database missing that store must reject the operation.
  • Keys must be valid native IndexedDB keys. Values must be structured-clone-compatible. Propagate native key, clone, schema and version errors as operation rejections.
  • get(key) resolves to the stored value. Missing keys resolve to undefined, and a stored null remains null.
  • set(key, value) uses put, overwrites an existing key and resolves to the stored key. delete(key) resolves to undefined, including when the key was absent.

Transaction and connection lifecycle

Each operation gets its own transaction. Resolve only on transaction completion, not request success. Request success can still be followed by abort. Reject an aborted transaction with its request/transaction error, or with DOMException named AbortError if explicit abort has no error value. A synchronous request-construction failure must reject and abort its transaction.

Use this bounded open policy:

  • Reject a blocked open with DOMException named InvalidStateError, asking the caller to close other connections before retrying. The native open request itself cannot be canceled directly.
  • Mark that attempt abandoned. If it later reaches upgradeneeded, abort the upgrade. If it nevertheless succeeds, close the connection. Do not silently migrate after reporting failure.
  • Forget failed open attempts so a later call can retry.
  • On a connection's versionchange, close it and clear the cached connection. Clear the cache on an unexpected close event too. An old-version wrapper used after a later upgrade must reject with VersionError, not keep using a stale connection.

No multi-operation transaction API, per-call cancellation or automatic retry of writes is required.

Local checks and cleanup

Edit tasks/indexeddb/starter.mjs and use Run browser checks in the supplied page. The check creates uniquely named memoized-async-browser-* databases and deletes only those names. It reports commit ordering, missing/null results, clone errors, real constraint failures, abort after request success, blocked upgrades and version changes.

For an automated real-browser run:

node checks/browser.mjs starter

The runner uses the existing Puppeteer installation and the Chrome executable configured in that file. It reports this task separately from the service-worker task. Do not count a Node mock as a browser pass.

For manual experiments outside the check, choose your own unique exercise database name. Await indexedDB.deleteDatabase(name) through its events when finished. A blocked delete means a connection must close first. Do not clear the entire origin. Close the practice tabs and stop only your own fixture process.

Starter: tasks/indexeddb/starter.mjs

export function idbStore({ db: dbName, store: storeName, version = 1 }) {
  return {
    get: async (key) => { throw new Error('Implement get') },
    set: async (key, value) => { throw new Error('Implement set') },
    delete: async (key) => { throw new Error('Implement delete') },
  }
}

Take a moment to think about this before revealing the answer

Explain your reasoning or try an implementation before comparing answers.

Explore the full Browser Storage material

Premium includes the complete lessons and implementation references. Free practice questions remain available without a subscription.

All course tracks & premium content
From basics to advanced masterclasses
Built for JS/TS developers like you
Real-world tips & common pitfalls
Upgrade to Premium