As a precursor to what you're about to read, I original wrote the majority of this post in February 2019 and in the subsequent 6½ years, this API matured enough that it was worthwhile finishing. I've only updated where appropriate.


Firstly, I apologise for the awful post title. Still - my excitement about this browser API is disproportionate to how utterly uncool it really is; the cookie browser API got a serious upgrade.

Fuuuq-king-finally. Amirite?

That bad smell

I've been complaining about the current document.cookie API for quite literally a decade. 10 years! That's some sign of how bad it is.

There's 40 billion packages published on npm, and just shy of 2% of those* are dedicated to setting and parsing cookies - it's that bad.

Figures may be exaggerated, or possibly made up.

When the Web Storage API started to land in browsers, I'd refer to them as Cookies on Steroids.

A few lowlights:

  • "Session" cookies leak across sessions
  • Non-session cookies require returning to the 70s via time machine to recall the exact date format to make them work
  • Deleting a cookie doesn't delete at all, just sets it to really old - has grandpa been deleted? Who knows.
  • The time format for the expire is exact, get it wrong, no errors, just doesn't work…or is it a session cookie, again who knows.
  • Syntax for setting cookie is brittle, i.e. Set-Cookie: path=/;token=1234 fails without explanation.

In fact, here's a slide straight out of my "Browsers with Wings" talk from 2010.

Fuck cookies

Yes. I am was upset about cookies.

Web Storage took a bit of a bashing too due to it's synchronous way of doing things. If you happened to try to store an entire house worth of data in localStorage, your browser would jump out and beat you on the head.

And so, apparently, IndexedDB would save us. If anyone could actually understand how it worked

† Of course the fabulous Jaffa The Cake-Archibald did just that with idb-keyval which just spoils all the fun of moaning about IDB…

Anyway, all that's changing because there's a new API in store…

The new CookieStore

Oh yes, a new store that not only buys and sells your cookies, your cookies come with all kinds of extra sprinklings, BUT ALSO the store tells you when new cookies are hot off the cookie pressing machine!

I am excited

I hear you: Enough with the bad puns, show me code.

The highlights

  • store and retrieve is async, which means no blocking the browser.
  • syntax is very simple (by comparison)
  • the API is also available from inside a service worker
  • there are APIs for getting all and getting individual named cookies
  • change events (this is the big one for me)
  • moved to baseline support in 2025

Getting a cookie

I was going to include how to get a cookie using the current API for comparison - but I decided I didn't hate myself enough, so let's look at getting a cookie.

As a note - all the cookie store methods return promises, so it's async by default (which means no locking up the browser during any deserialization).

const token = await cookieStore.get('token');
// response:
{
  "domain": null,
  "expires": 1549643944000,
  "name": "token",
  "path": "/",
  "sameSite": "unrestricted",
  "secure": false,
  "value": "eyJhbGciOiJIUzI1NiIsIn…"
}

That's it.

Setting cookies

Rather than the strange "set it in the past for session", by default if you set a cookie, it's session based.

Session cookies are very simple:

await cookieStore.set('token', 'very_secret');

// also works:
await cookieStore.set({ name: 'token', value: 'very_secret' });

It becomes more interesting for cookies with expiries. A non-session cookie requires that an object is passed in with options. In particular, the expires must be a unix timestamp, so getting the wrong datetime format only results in an error (thankfully):

await cookieStore.set({ name: 'token', value: 'very_secret', expires: "next week"});
// Uncaught (in promise) TypeError: CookieStore.set: 'expires' member of CookieInit is not a finite floating-point value.

Caveat: it's worth stating that just like the Storage API values are serialised to strings, so values will need to be appropriately parsed back out.

Setting a cookie for 30 days is immediately (to me) much more intuitive:

const oneDay = 1000 * 60 * 60 * 24
await cookieStore.set({ name: 'token', value: 'very_secret', expires: Date.now() + oneDay * 30 });

Can I use?

Well… it's (at time of writing) so freakingly fresh out of the API oven that not even caniuse.com has it listed as a thing, but apparently web developers are giving positive signals (though positive signals about the API or cookies in general is unclear).

Certainly it's available in Chrome 69 and upwards (from chromestatus.com), and I've written a handy polyfill for you.

The (massive) downside of the polyfill is that it doesn't support events properly (only fires when you modify a cookie through the polyfill), and it doesn't include all the juicy data about the cookie, like the expiry, path and so on. But if you want to set and get, then this will get you started.

If you want to read more about this API, there's also an excellent explainer document which, perhaps, could have saved you from reading my ramblings.

Drafts may be incomplete or entirely abandoned, so please forgive me. If you find an issue with a draft, or would like to see me write about something specifically, please try raising an issue.