How to Manage Cookies in JS: Complete Tutorial

A complete tutorial on managing cookies in JavaScript. Covers document.cookie API, setting cookies with path, domain, expires, max-age attributes, Secure and HttpOnly and SameSite flags, reading and parsing cookies, cookie encoding, building a cookie utility library, and understanding cookie security best practices.

JavaScriptintermediate
15 min read

Cookies are small pieces of data stored in the browser and sent with every HTTP request to the matching domain. Unlike localStorage or sessionStorage, cookies participate in the HTTP protocol. This guide covers reading, writing, and deleting cookies from JavaScript.

javascriptjavascript
// Basic cookie (session cookie, deleted when browser closes)
document.cookie = "username=parth";
 
// Cookie with expiration (30 days)
const expires = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toUTCString();
document.cookie = `theme=dark; expires=${expires}; path=/`;
 
// Cookie with max-age (seconds)
document.cookie = "language=en; max-age=2592000; path=/"; // 30 days
 
// Full cookie with all attributes
document.cookie = [
  "sessionToken=abc123",
  "path=/",
  "domain=.example.com",
  `expires=${new Date(Date.now() + 7 * 86400000).toUTCString()}`,
  "secure",
  "samesite=strict",
].join("; ");

Despite the = syntax, document.cookie does not overwrite all cookies. Each assignment adds or updates a single cookie identified by its name, path, and domain combination.

AttributeValuePurpose
pathURL path (e.g., /)Cookie sent only for matching paths
domainDomain stringCookie accessible on this domain and subdomains
expiresUTC date stringCookie removed after this date
max-ageSeconds (integer)Cookie removed after this many seconds
secure(no value)Cookie sent only over HTTPS
httponly(no value)Cookie inaccessible to JavaScript (server-set only)
samesitestrict, lax, noneControls cross-site cookie sending

SameSite Explained

SameSite ValueBehavior
strictCookie never sent on cross-site requests
laxCookie sent on top-level navigations (GET only)
noneCookie sent on all cross-site requests (requires secure)
javascriptjavascript
// Strict: best for sensitive operations
document.cookie = "csrfToken=xyz789; samesite=strict; secure; path=/";
 
// Lax: good default for most cookies
document.cookie = "preferences=dark; samesite=lax; path=/";
 
// None: for cross-site scenarios (requires Secure)
document.cookie = "tracking=abc; samesite=none; secure; path=/";

Reading Cookies

document.cookie returns all accessible cookies as a single semicolon-separated string:

javascriptjavascript
console.log(document.cookie);
// "username=parth; theme=dark; language=en"
javascriptjavascript
function getCookie(name) {
  const cookies = document.cookie.split("; ");
 
  for (const cookie of cookies) {
    const [key, ...valueParts] = cookie.split("=");
    if (key === name) {
      return decodeURIComponent(valueParts.join("="));
    }
  }
 
  return null;
}
 
const theme = getCookie("theme"); // "dark"
const missing = getCookie("nonexistent"); // null

Parsing All Cookies

javascriptjavascript
function getAllCookies() {
  if (!document.cookie) return {};
 
  return document.cookie.split("; ").reduce((cookies, cookie) => {
    const [key, ...valueParts] = cookie.split("=");
    cookies[decodeURIComponent(key)] = decodeURIComponent(
      valueParts.join("=")
    );
    return cookies;
  }, {});
}
 
const all = getAllCookies();
console.log(all); // { username: "parth", theme: "dark", language: "en" }

Encoding Special Characters

Cookie values cannot contain semicolons, commas, or spaces without encoding:

javascriptjavascript
// Encode values with special characters
function setCookie(name, value, options = {}) {
  let cookieString = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
 
  if (options.maxAge !== undefined) {
    cookieString += `; max-age=${options.maxAge}`;
  }
 
  if (options.expires) {
    cookieString += `; expires=${options.expires.toUTCString()}`;
  }
 
  if (options.path) {
    cookieString += `; path=${options.path}`;
  }
 
  if (options.domain) {
    cookieString += `; domain=${options.domain}`;
  }
 
  if (options.secure) {
    cookieString += "; secure";
  }
 
  if (options.sameSite) {
    cookieString += `; samesite=${options.sameSite}`;
  }
 
  document.cookie = cookieString;
}
 
// Safe to store values with special characters
setCookie("search", "hello world; more=stuff", {
  maxAge: 86400,
  path: "/",
  secure: true,
  sameSite: "lax",
});

Deleting Cookies

Cookies are deleted by setting their max-age to 0 or their expires to a past date. The path and domain must match the original cookie:

javascriptjavascript
function deleteCookie(name, path = "/", domain = undefined) {
  let cookieString = `${encodeURIComponent(name)}=; max-age=0; path=${path}`;
 
  if (domain) {
    cookieString += `; domain=${domain}`;
  }
 
  document.cookie = cookieString;
}
 
// Delete the "theme" cookie
deleteCookie("theme");
 
// Delete a cookie on a specific domain
deleteCookie("tracking", "/", ".example.com");

For advanced parsing and deletion patterns, see parsing and deleting browser cookies with JS.

javascriptjavascript
class CookieManager {
  constructor(defaults = {}) {
    this.defaults = {
      path: "/",
      secure: true,
      sameSite: "lax",
      ...defaults,
    };
  }
 
  get(name) {
    const cookies = document.cookie.split("; ");
    for (const cookie of cookies) {
      const [key, ...valueParts] = cookie.split("=");
      if (decodeURIComponent(key) === name) {
        return decodeURIComponent(valueParts.join("="));
      }
    }
    return null;
  }
 
  getAll() {
    if (!document.cookie) return {};
    return document.cookie.split("; ").reduce((map, cookie) => {
      const [key, ...parts] = cookie.split("=");
      map[decodeURIComponent(key)] = decodeURIComponent(parts.join("="));
      return map;
    }, {});
  }
 
  set(name, value, options = {}) {
    const opts = { ...this.defaults, ...options };
    let str = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
 
    if (opts.maxAge !== undefined) str += `; max-age=${opts.maxAge}`;
    if (opts.expires) str += `; expires=${opts.expires.toUTCString()}`;
    if (opts.path) str += `; path=${opts.path}`;
    if (opts.domain) str += `; domain=${opts.domain}`;
    if (opts.secure) str += "; secure";
    if (opts.sameSite) str += `; samesite=${opts.sameSite}`;
 
    document.cookie = str;
  }
 
  delete(name, options = {}) {
    const opts = { ...this.defaults, ...options };
    let str = `${encodeURIComponent(name)}=; max-age=0`;
    if (opts.path) str += `; path=${opts.path}`;
    if (opts.domain) str += `; domain=${opts.domain}`;
    document.cookie = str;
  }
 
  has(name) {
    return this.get(name) !== null;
  }
 
  setJSON(name, value, options = {}) {
    this.set(name, JSON.stringify(value), options);
  }
 
  getJSON(name, fallback = null) {
    const raw = this.get(name);
    if (raw === null) return fallback;
    try {
      return JSON.parse(raw);
    } catch {
      return fallback;
    }
  }
}
 
// Usage
const cookies = new CookieManager({ path: "/", secure: true });
 
cookies.set("theme", "dark", { maxAge: 30 * 86400 }); // 30 days
cookies.setJSON("prefs", { fontSize: 16, sidebar: true }, { maxAge: 86400 });
 
console.log(cookies.get("theme")); // "dark"
console.log(cookies.getJSON("prefs")); // { fontSize: 16, sidebar: true }
 
cookies.delete("theme");

Cookies vs Web Storage Comparison

FeatureCookieslocalStoragesessionStorage
Size limit~4KB per cookie~5MB~5MB
Sent with HTTP requestsYesNoNo
Accessible from serverYesNoNo
Accessible from JSYes (unless HttpOnly)YesYes
Expiration controlYesManual onlyTab close
Cross-tabYesYesNo
Rune AI

Rune AI

Key Insights

  • document.cookie is append-only per assignment: Each document.cookie = ... sets or updates one cookie; it does not overwrite all cookies
  • Always encode values: Use encodeURIComponent for names and values to safely handle semicolons, commas, and spaces
  • Match path and domain when deleting: Cookies are identified by name + path + domain; mismatched attributes create new entries instead of deleting
  • SameSite=Lax as default: Protects against CSRF while allowing top-level navigations; use Strict for sensitive operations, None only with Secure
  • ~4KB limit per cookie: For larger client-side data, use localStorage (~5MB) or IndexedDB; cookies are for small, server-relevant values
RunePowered by Rune AI

Frequently Asked Questions

What is the difference between expires and max-age?

`expires` takes a UTC date string and removes the cookie at that specific time. `max-age` takes a number of seconds from the current moment. If both are set, `max-age` takes precedence in modern browsers. Use `max-age` for simpler code.

Can JavaScript read HttpOnly cookies?

No. HttpOnly cookies are invisible to `document.cookie`. They can only be set and read by the server via HTTP headers. This protects session tokens from XSS attacks. For client-side preferences, use regular cookies without HttpOnly or use localStorage.

How many cookies can I set per domain?

Most browsers allow at least 50 cookies per domain with a total size limit of ~4KB per cookie. The exact limits vary by browser. If you need to store more data per domain, use [JS localStorage API guide](/tutorials/programming-languages/javascript/js-localstorage-api-guide-a-complete-tutorial) or IndexedDB.

Why do I need to match path and domain when deleting a cookie?

cookie is identified by its name + path + domain combination. Setting `max-age=0` on a cookie with a different path than the original will create a new expired cookie instead of deleting the existing one. Always match the original path and domain when deleting.

Should I use cookies or localStorage for user preferences?

Use cookies if the server needs to know the preference (e.g., language for server-rendered pages, currency for pricing). Use localStorage if the preference is purely client-side (e.g., sidebar collapsed state, code editor theme). See [JS localStorage API guide](/tutorials/programming-languages/javascript/js-localstorage-api-guide-a-complete-tutorial) for localStorage patterns.

Conclusion

Cookies remain essential for server-communicated state like authentication tokens, language preferences, and consent flags. Use document.cookie with proper encoding, always set path=/ and samesite=lax at minimum, and prefer max-age over expires for expiration. For parsing and deletion utilities, see parsing and deleting browser cookies with JS. For client-only storage, prefer JS localStorage API guide or JS sessionStorage API guide.