Stop Regex-Parsing document.cookie. Use CookieStore
document.cookie hands you one flat string and no way to know when it changes. The Cookie Store API gives you async get/set/delete and a change event — but its set() defaults to a stricter SameSite than the one you're used to.

Open any codebase old enough to have cookies in it and grep for document.cookie. You will find a function that looks like this, written by someone who is no longer at the company:
It works. It has worked since 2011. It also has a small, well-known list of ways to get it wrong — cookie names that are prefixes of each other, values with unescaped = or ;, whitespace after the semicolon depending on which browser wrote the header. Everyone's seen at least one of these bugs. Nobody rewrites the function, because it's not broken today.
Here's the harder problem that regex can't fix at all: you have no way to know when a cookie changes. Not from another tab. Not from a Set-Cookie header on a fetch() response. Not even from a second script on your own page calling document.cookie = ... a moment after yours did. document.cookie is a plain string property. Reading it tells you the current state. It has never told you when the state moved.
Once the "I need to react to cookie changes" requirement shows up — a login cookie set by an API call, a consent banner another tab just dismissed — the usual fixes are:
- Poll
document.cookieon an interval. It works, in the sense that asetIntervalchecking a string every 500ms will eventually notice a change. It also means every tab of every user is now diffing a string forever for an event that might happen once a session. - Have the server tell the client via a WebSocket or SSE. Real infrastructure for a problem that's purely local — the cookie already changed on this machine, in this browser, you just don't have a hook for it.
- Wrap every single place that writes a cookie in your own pub/sub. This can work, right up until a third-party script, a
Set-Cookieresponse header, or literally the browser's own cookie-jar expiry logic changes a cookie your pub/sub doesn't know about.
All three treat "the browser won't tell me" as something to engineer around. It's worth asking why the browser won't tell you in the first place — and it turns out, more recently, it will.
Chrome and Edge ship window.cookieStore (and self.cookieStore inside a service worker) — a promise-based Cookie Store API that treats cookies as structured objects instead of one string you serialize by hand.
Reading is no longer a regex:
Writing takes an object instead of a hand-built key=value; path=...; expires=... string:
And the part document.cookie could never do — a real event:
That listener fires for cookies your page sets, cookies a fetch() response set via Set-Cookie, and cookies removed by expiry — no polling, no pub/sub you wrote yourself. A consent banner that gets dismissed in one tab can now update every other open tab of the same origin the moment it happens.
Runs right in your browser — poke at it and watch the concept react live.
Here's the thing that actually catches people moving existing code over, and it's not a bug — it's a deliberate spec choice that reads like a footnote until it breaks something.
When you write a cookie the old way, through a Set-Cookie header or document.cookie, and you don't specify SameSite, browsers default it to Lax. That's been true for years — it's why a cookie set on your site still rides along when a user clicks a plain link to your site from somewhere else, but doesn't get sent on a cross-site POST.
cookieStore.set() doesn't inherit that default. Per the spec, if you don't pass sameSite explicitly, it defaults to "strict" — stricter than what document.cookie gives you for free. A Strict cookie is withheld on any cross-site navigation, top-level link clicks included.
So the failure mode looks like this: you migrate a cookie-setting line from document.cookie = "..." to cookieStore.set({...}), run your test suite, ship it. Everything that happens inside your own site keeps working, because same-site requests don't care about SameSite at all. Weeks later, someone clicks a link to your site from an email or a partner site, lands on a page that expects that cookie to already be there, and it isn't. No error. No console warning. The cookie you set is simply not attached to that request, because Strict said not to.
The fix is one keyword, once you know to look for it:
- It's Chromium-only right now. Chrome and Edge support
cookieStore; Firefox and Safari don't ship it as of this writing. Check the current numbers on caniuse before you rely on it for anything that isn't wrapped in a feature check —if ("cookieStore" in window)— with adocument.cookiefallback. - It requires a secure context. Like most newer, more capable browser APIs,
cookieStoresimply isn't there on plainhttp://origins outsidelocalhost. If it'sundefinedin production but present when you test locally, that's almost certainly why.
document.cookie was never designed to be parsed — it's a string interface bolted onto a feature that predates JSON.parse existing. cookieStore treats cookies as the structured, awaitable, observable data they actually are, and the change event alone is worth the migration for anything that needs to react to a cookie set outside your own code. Just don't let sameSite default silently to something stricter than the behavior you were relying on.
Does your codebase still have a hand-rolled cookie parser in it? How old is it, and does anyone remember writing it?
Think it clicked? Take the 7-question quiz →
Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.
🚀 Want more like this? Every guide, playground, and quiz lives on bestpractic.org — open it and sign up free so the next one finds you.
Thanks for reading! Let's stay connected:
- ⭐ GitHub — follow me and star the projects: github.com/parsajiravand
- 💬 Discord — join the frontend best-practices community: discord.gg/d9KRhuAwQ
- 📸 Instagram — frontend best practices, daily: @bestpractice___
Keep reading
One post a day, in your inbox
Each one with a runnable playground and a quiz. No pitch, no digest, unsubscribe in one click.
0 comments
Sign in to join the discussion, like comments, and save articles for later.