The Cookie Store API
The problem with document.cookie
Section titled “The problem with document.cookie”document.cookie is a synchronous API designed in the mid-1990s. It has three well-known friction points:
- Parsing is manual — reading a specific cookie requires splitting and looping.
- Writes are string-based — you build a cookie string by hand.
- No change events — there is no way to observe when another tab or the server changes a cookie.
The Cookie Store API solves all three with a clean, Promise-based interface.
Browser support
Section titled “Browser support”The Cookie Store API ships in Chrome and Edge (Chromium-based browsers). Firefox does not support it yet. Always feature-detect before using it.
cookieStore.get — read a single cookie
Section titled “cookieStore.get — read a single cookie”Returns a Promise that resolves to a CookieListItem object or null if the cookie does not exist:
const cookie = await cookieStore.get('theme');if (cookie) { console.log(cookie.name); // 'theme' console.log(cookie.value); // 'dark'}The returned object also includes domain, path, expires, secure, sameSite, and httpOnly properties.
cookieStore.set — write a cookie
Section titled “cookieStore.set — write a cookie”Accepts a name/value string pair or an options object:
// Simple formawait cookieStore.set('theme', 'dark');
// Options form — recommended for control over attributesawait cookieStore.set({ name: 'theme', value: 'dark', maxAge: 86400, sameSite: 'lax',});Returns a Promise that resolves when the cookie is written.
cookieStore.delete — remove a cookie
Section titled “cookieStore.delete — remove a cookie”await cookieStore.delete('theme');Returns a Promise that resolves when the cookie is deleted. This is equivalent to setting Max-Age=0.
cookieStore.getAll — read all cookies
Section titled “cookieStore.getAll — read all cookies”const allCookies = await cookieStore.getAll();allCookies.forEach(c => console.log(c.name, c.value));Unlike document.cookie, this returns a proper array of objects — no string parsing required.
Change events
Section titled “Change events”The Cookie Store API allows you to listen for cookie changes reactively:
cookieStore.addEventListener('change', event => { event.changed.forEach(c => { console.log('Cookie changed:', c.name, '=', c.value); }); event.deleted.forEach(c => { console.log('Cookie deleted:', c.name); });});event.changed is an array of cookies that were set or updated. event.deleted is an array of cookies that were removed. This works across tabs for cookies that are not scoped to a single tab.
Feature-detect pattern
Section titled “Feature-detect pattern”Because browser support is incomplete, always feature-detect and fall back to document.cookie:
async function getTheme() { if ('cookieStore' in window) { const c = await cookieStore.get('theme'); return c ? c.value : null; } else { // document.cookie fallback var pairs = document.cookie.split('; '); for (var i = 0; i < pairs.length; i++) { var p = pairs[i].split('='); if (p[0] === 'theme') return decodeURIComponent(p[1]); } return null; }}Runnable demo: cookieStore with fallback
Section titled “Runnable demo: cookieStore with fallback”Trade-offs
Section titled “Trade-offs”| Option | Benefit | Cost |
|---|---|---|
| Cookie Store API | Much shorter, more readable code — await cookieStore.get() instead of splitting a string yourself, plus a reactive change event to listen to | Not supported in Firefox — you must feature-detect and always write a document.cookie fallback, doubling the code for the same logic |
document.cookie | Works in every browser, no fallback needed | A synchronous string API you must parse yourself every time, with no built-in change event |
Common mistakes
Section titled “Common mistakes”- Calling
cookieStore.get()/.set()without feature-detecting first — the code breaks immediately in Firefox, sincecookieStoredoesn’t exist onwindow. - Forgetting that every Cookie Store API method is async — writing code as if the value comes back synchronously gets you a Promise object instead of the real value; always
awaitit. - Assuming the
changeevent behaves identically across every browser and situation — this event’s cross-tab behavior isn’t fully standardized across implementations yet, so test it directly before relying on it as core logic.