A cookie is a small name-value pair (usually at most 4 KB) sent with matching requests in the Cookie header. Servers set cookies with Set-Cookie; scripts use the quirky document.cookie accessor. Attributes set scope and lifetime: Expires or Max-Age (neither means a session cookie), Domain, Path, Secure, HttpOnly (hidden from scripts). SameSite=Strict limits it to same-site requests, Lax adds top-level GET navigations from other sites, and None (which requires Secure) sends it everywhere. Chrome 1 treats a cookie without the attribute as Lax; Firefox 555 and Safari 10 do not by default.
CHIPS (Cookies Having Independent Partitioned State) keeps embeds working without third-party cookies. A Partitioned cookie is also keyed by the top-level site, so a widget embedded on two shops gets two jars and cannot track users across them: Set-Cookie: __Host-chat=a1; Secure; Path=/; SameSite=None; Partitioned (Chrome 114, Firefox 141, Safari 26.2). Security covers cookie security.
The Cookie Store API replaces string parsing with promises, fires change events and works in service workers. MDN 102 marks it Baseline 2025 (Chrome 87, Firefox 140, Safari 18.4), though only Chromium 4,389 returns metadata such as path and expires.
document.cookie = 'theme=dark; max-age=86400; path=/; samesite=lax'; // adds ONE cookie
document.cookie = `note=${encodeURIComponent('a;b=c')}`; // encode ; = and spaces
console.log(document.cookie); // only "name=value; ..."
cookieStore.onchange = (e) => console.log('change:', [...e.changed, ...e.deleted][0].name);
await cookieStore.set({ name: 'cart', value: '3', expires: Date.now() + 7 * 864e5 });
const cart = await cookieStore.get('cart');
console.log(cart.value, cart.sameSite); // default is strict
await cookieStore.delete('theme');theme=dark; note=a%3Bb%3Dc change: cart 3 strict change: theme
Store an identifier, not data, and mark session cookies HttpOnly; Secure; SameSite=Lax.