js-cookie vs react-cookie vs universal-cookie
Client-Side Cookie Management in React and Vanilla JavaScript
js-cookiereact-cookieuniversal-cookieSimilar Packages:

Client-Side Cookie Management in React and Vanilla JavaScript

js-cookie, react-cookie, and universal-cookie are libraries designed to simplify reading, writing, and deleting browser cookies. js-cookie is a lightweight, framework-agnostic library focused on simplicity and API consistency across browsers. react-cookie provides React hooks and context providers to manage cookies within the component lifecycle, often relying on universal-cookie for the underlying logic. universal-cookie serves as the core engine that works in both Node.js (for Server-Side Rendering) and the browser, enabling isomorphic cookie handling without framework dependencies.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
js-cookie022,71926.2 kB33 years agoMIT
react-cookie021378.6 kB310 days agoMIT
universal-cookie021365.6 kB310 days agoMIT

Cookie Management Libraries: js-cookie vs react-cookie vs universal-cookie

Handling cookies in modern web development requires balancing simplicity, framework integration, and server-side compatibility. js-cookie, react-cookie, and universal-cookie each solve this problem differently. Let's compare how they handle common engineering scenarios.

πŸ› οΈ API Style: Simple Functions vs React Hooks vs Class Instances

js-cookie uses a simple, static API.

  • You call methods directly on the Cookies object.
  • No instantiation required, making it very quick to drop into any script.
// js-cookie: Static API
import Cookies from 'js-cookie';

Cookies.set('session_id', 'abc123', { expires: 7 });
const value = Cookies.get('session_id');

react-cookie uses React hooks to bind cookie state to components.

  • You get an array with cookies, a setter, and a remover.
  • Updates trigger re-renders automatically when cookies change.
// react-cookie: Hook-based API
import { useCookies } from 'react-cookie';

function Profile() {
  const [cookies, setCookie, removeCookie] = useCookies(['session_id']);
  
  const update = () => setCookie('session_id', 'abc123', { path: '/' });
  return <div>{cookies.session_id}</div>;
}

universal-cookie uses a class instance that you create manually.

  • You instantiate new Cookies() to get a handle.
  • Gives you full control over the instance, useful for passing around in non-React code.
// universal-cookie: Class-based API
import Cookies from 'universal-cookie';

const cookies = new Cookies();
cookies.set('session_id', 'abc123', { path: '/' });
const value = cookies.get('session_id');

🌐 Server-Side Rendering (SSR): Browser Only vs Isomorphic

js-cookie works only in the browser.

  • It accesses document.cookie directly.
  • Will throw errors if imported in a Node.js server environment without checks.
// js-cookie: Browser only
// This will fail in Node.js
import Cookies from 'js-cookie';
// Error: document is not defined (if run on server)

react-cookie supports SSR but requires a provider.

  • You must wrap your app in CookiesProvider to pass initial cookies from server to client.
  • Handles hydration automatically if configured correctly.
// react-cookie: SSR with Provider
import { CookiesProvider } from 'react-cookie';

function App() {
  return <CookiesProvider><Dashboard /></CookiesProvider>;
}

universal-cookie is built for isomorphic usage.

  • It accepts an optional cookie string from the server (e.g., from request headers).
  • Ideal for custom SSR setups where you need to parse cookies in Node.js manually.
// universal-cookie: Isomorphic
import Cookies from 'universal-cookie';

// On Server
const cookies = new Cookies(req.headers.cookie);

// On Client
const cookies = new Cookies();

βš›οΈ React Integration: None vs Native Hooks vs Manual

js-cookie has no React awareness.

  • Changing a cookie does not trigger a component update.
  • You must manually manage state or force re-renders if UI depends on cookie values.
// js-cookie: No React integration
function User() {
  const [name, setName] = useState('');
  
  // Must manually sync
  const load = () => setName(Cookies.get('name'));
  return <div>{name}</div>;
}

react-cookie is built for React.

  • Hooks like useCookies track dependencies.
  • Setting a cookie updates the component state automatically.
// react-cookie: Auto-updates UI
function User() {
  const [cookies, setCookie] = useCookies(['name']);
  // Component re-renders when 'name' cookie changes
  return <div>{cookies.name}</div>;
}

universal-cookie requires manual wiring in React.

  • You can use it inside useEffect, but it won't trigger renders on its own.
  • Often used as the engine inside custom hooks or react-cookie itself.
// universal-cookie: Manual React wiring
function User() {
  const [name, setName] = useState('');
  
  useEffect(() => {
    const cookies = new Cookies();
    setName(cookies.get('name'));
  }, []);
  
  return <div>{name}</div>;
}

πŸ“¦ Dependencies and Footprint

js-cookie has zero dependencies.

  • Extremely small and focused.
  • No React or Node.js specific code included.
// js-cookie: Zero dependencies
// Import is standalone
import Cookies from 'js-cookie';

react-cookie depends on universal-cookie.

  • It wraps the core logic to add React-specific features.
  • Heavier than js-cookie due to React context and hook logic.
// react-cookie: Depends on universal-cookie
// Internally uses universal-cookie instance
import { useCookies } from 'react-cookie';

universal-cookie has minimal dependencies.

  • Focuses on cookie parsing and setting logic.
  • Lighter than react-cookie but heavier than js-cookie due to SSR support.
// universal-cookie: Minimal deps
import Cookies from 'universal-cookie';

🀝 Similarities: Shared Ground Between Libraries

While the implementation differs, all three libraries share core goals and behaviors.

1. πŸͺ Standard Cookie Attributes

  • All support expires, path, domain, secure, and sameSite.
  • Ensure consistent security settings across implementations.
// js-cookie
Cookies.set('id', '1', { secure: true, sameSite: 'strict' });

// react-cookie
setCookie('id', '1', { secure: true, sameSite: 'strict' });

// universal-cookie
cookies.set('id', '1', { secure: true, sameSite: 'strict' });

2. πŸ—‘οΈ Removing Cookies

  • All provide a method to delete cookies by setting expiration to past.
  • Requires matching the path and domain used when setting.
// js-cookie
Cookies.remove('id');

// react-cookie
removeCookie('id');

// universal-cookie
cookies.remove('id');

3. πŸ”’ Encoding Support

  • All handle encoding special characters in cookie values automatically.
  • Prevents issues with spaces or symbols in stored data.
// js-cookie
Cookies.set('data', '{ "user": "john" }');

// react-cookie
setCookie('data', '{ "user": "john" }');

// universal-cookie
cookies.set('data', '{ "user": "john" }');

4. πŸ§ͺ Testing Friendliness

  • All can be mocked easily in unit tests.
  • js-cookie and universal-cookie allow injecting custom storage backends.
// universal-cookie: Custom backend
const cookies = new Cookies(null, { path: '/' });

// js-cookie: Custom attributes
Cookies.set('test', 'value', { path: '/test' });

5. 🌍 Browser Compatibility

  • All support modern browsers and handle legacy edge cases.
  • Abstract away differences in how browsers handle cookie limits.
// All libraries handle this internally
// No need for browser sniffing
Cookies.set('key', 'value');

πŸ“Š Summary: Key Similarities

FeatureShared by All Three
Core OpsπŸͺ Get, Set, Remove
AttributesπŸ”’ Secure, SameSite, Path
Encoding🧡 Auto-encoding values
Compatibility🌍 Modern + Legacy Browsers
TestingπŸ§ͺ Mockable APIs

πŸ†š Summary: Key Differences

Featurejs-cookiereact-cookieuniversal-cookie
API StyleπŸ› οΈ Static Functionsβš›οΈ React HooksπŸ—οΈ Class Instance
React Aware❌ Noβœ… Yes❌ No
SSR Ready❌ Browser Onlyβœ… Yes (with Provider)βœ… Yes (Native)
Dependencies0️⃣ ZeroπŸ“¦ React + universal-cookieπŸ“¦ Minimal
Reactivity⏳ Manual State Sync⚑ Auto Re-render⏳ Manual State Sync

πŸ’‘ The Big Picture

js-cookie is like a Swiss Army knife πŸ”ͺβ€”simple, reliable, and works anywhere JavaScript runs in the browser. Best for vanilla projects, simple scripts, or when you want zero framework lock-in.

react-cookie is like a power tool for React πŸͺ›β€”integrates directly into your component lifecycle. Best for React SPAs where cookie changes need to update the UI immediately without boilerplate.

universal-cookie is like the engine under the hood πŸŽοΈβ€”powers react-cookie but available for custom use. Best for SSR applications (Next.js, Remix) where you need direct access to cookie parsing on the server.

Final Thought: If you are in React and need SSR, universal-cookie (often via react-cookie) is the standard. If you are in vanilla JS or just need a lightweight client-side tool, js-cookie remains the gold standard for simplicity.

How to Choose: js-cookie vs react-cookie vs universal-cookie

  • js-cookie:

    Choose js-cookie for vanilla JavaScript projects or when you need a lightweight, zero-dependency solution that works consistently across all browsers. It is ideal for simple cookie operations where React integration or Server-Side Rendering (SSR) context is not required, offering the smallest footprint and simplest API.

  • react-cookie:

    Choose react-cookie if you are building a React application and want to manage cookies using hooks like useCookies within your components. It is best suited for client-side heavy React apps where you need reactive updates when cookie values change, provided you do not need deep custom SSR logic beyond what the library provides.

  • universal-cookie:

    Choose universal-cookie if you need isomorphic cookie handling that works seamlessly in both Node.js (for Next.js, Remix, or custom SSR) and the browser. It is the underlying engine for react-cookie and is the right choice when you need direct control over cookie instances in server components or non-React Node.js environments.

README for js-cookie

JavaScript Cookie CI BrowserStack JavaScript Style Guide Code Climate npm size jsDelivr Hits

A simple, lightweight JavaScript API for handling cookies

πŸ‘‰πŸ‘‰ If you're viewing this at https://github.com/js-cookie/js-cookie, you're reading the documentation for the main branch. View documentation for the latest release. πŸ‘ˆπŸ‘ˆ

Installation

NPM

JavaScript Cookie supports npm under the name js-cookie.

npm i js-cookie

The npm package has a module field pointing to an ES module variant of the library, mainly to provide support for ES module aware bundlers, whereas its browser field points to an UMD module for full backward compatibility.

Not all browsers support ES modules natively yet. For this reason the npm package/release provides both the ES and UMD module variant and you may want to include the ES module along with the UMD fallback to account for this:

CDN

Alternatively, include js-cookie via jsDelivr CDN.

Basic Usage

Create a cookie, valid across the entire site:

Cookies.set('name', 'value')

Create a cookie that expires 7 days from now, valid across the entire site:

Cookies.set('name', 'value', { expires: 7 })

Create an expiring cookie, valid to the path of the current page:

Cookies.set('name', 'value', { expires: 7, path: '' })

Read cookie:

Cookies.get('name') // => 'value'
Cookies.get('nothing') // => undefined

Read all visible cookies:

Cookies.get() // => { name: 'value' }

Note: It is not possible to read a particular cookie by passing one of the cookie attributes (which may or may not have been used when writing the cookie in question):

Cookies.get('foo', { domain: 'sub.example.com' }) // `domain` won't have any effect...!

The cookie with the name foo will only be available on .get() if it's visible from where the code is called; the domain and/or path attribute will not have an effect when reading.

Delete cookie:

Cookies.remove('name')

Delete a cookie valid to the path of the current page:

Cookies.set('name', 'value', { path: '' })
Cookies.remove('name') // fail!
Cookies.remove('name', { path: '' }) // removed!

IMPORTANT! When deleting a cookie and you're not relying on the default attributes, you must pass the exact same path and domain attributes that were used to set the cookie:

Cookies.remove('name', { path: '', domain: '.yourdomain.com' })

Note: Removing a nonexistent cookie neither raises any exception nor returns any value.

Namespace conflicts

If there is any danger of a conflict with the namespace Cookies, the noConflict method will allow you to define a new namespace and preserve the original one. This is especially useful when running the script on third party sites e.g. as part of a widget or SDK.

// Assign the js-cookie api to a different variable and restore the original "window.Cookies"
var Cookies2 = Cookies.noConflict()
Cookies2.set('name', 'value')

Note: The .noConflict method is not necessary when using AMD or CommonJS, thus it is not exposed in those environments.

Encoding

This project is RFC 6265 compliant. All special characters that are not allowed in the cookie-name or cookie-value are encoded with each one's UTF-8 Hex equivalent using percent-encoding.
The only character in cookie-name or cookie-value that is allowed and still encoded is the percent % character, it is escaped in order to interpret percent input as literal.
Please note that the default encoding/decoding strategy is meant to be interoperable only between cookies that are read/written by js-cookie. To override the default encoding/decoding strategy you need to use a converter.

Note: According to RFC 6265, your cookies may get deleted if they are too big or there are too many cookies in the same domain, more details here.

Cookie Attributes

Cookie attribute defaults can be set globally by creating an instance of the api via withAttributes(), or individually for each call to Cookies.set(...) by passing a plain object as the last argument. Per-call attributes override the default attributes.

expires

Define when the cookie will be removed. Value must be a Number which will be interpreted as days from time of creation or a Date instance. If omitted, the cookie becomes a session cookie.

To create a cookie that expires in less than a day, you can check the FAQ on the Wiki.

Default: Cookie is removed when the user closes the browser.

Examples:

Cookies.set('name', 'value', { expires: 365 })
Cookies.get('name') // => 'value'
Cookies.remove('name')

path

A String indicating the path where the cookie is visible.

Default: /

Examples:

Cookies.set('name', 'value', { path: '' })
Cookies.get('name') // => 'value'
Cookies.remove('name', { path: '' })

Note regarding Internet Explorer:

Due to an obscure bug in the underlying WinINET InternetGetCookie implementation, IE’s document.cookie will not return a cookie if it was set with a path attribute containing a filename.

(From Internet Explorer Cookie Internals (FAQ))

This means one cannot set a path using window.location.pathname in case such pathname contains a filename like so: /check.html (or at least, such cookie cannot be read correctly).

In fact, you should never allow untrusted input to set the cookie attributes or you might be exposed to a XSS attack.

domain

A String indicating a valid domain where the cookie should be visible. The cookie will also be visible to all subdomains.

Default: Cookie is visible only to the domain or subdomain of the page where the cookie was created, except for Internet Explorer (see below).

Examples:

Assuming a cookie that is being created on site.com:

Cookies.set('name', 'value', { domain: 'subdomain.site.com' })
Cookies.get('name') // => undefined (need to read at 'subdomain.site.com')

Note regarding Internet Explorer default behavior:

Q3: If I don’t specify a DOMAIN attribute (for) a cookie, IE sends it to all nested subdomains anyway?
A: Yes, a cookie set on example.com will be sent to sub2.sub1.example.com.
Internet Explorer differs from other browsers in this regard.

(From Internet Explorer Cookie Internals (FAQ))

This means that if you omit the domain attribute, it will be visible for a subdomain in IE.

secure

Either true or false, indicating if the cookie transmission requires a secure protocol (https).

Default: No secure protocol requirement.

Examples:

Cookies.set('name', 'value', { secure: true })
Cookies.get('name') // => 'value'
Cookies.remove('name')

sameSite

A String, allowing to control whether the browser is sending a cookie along with cross-site requests.

Default: not set.

Note that more recent browsers are making "Lax" the default value even without specifiying anything here.

Examples:

Cookies.set('name', 'value', { sameSite: 'strict' })
Cookies.get('name') // => 'value'
Cookies.remove('name')

Setting up defaults

const api = Cookies.withAttributes({ path: '/', domain: '.example.com' })

Converters

Read

Create a new instance of the api that overrides the default decoding implementation. All get methods that rely in a proper decoding to work, such as Cookies.get() and Cookies.get('name'), will run the given converter for each cookie. The returned value will be used as the cookie value.

Example from reading one of the cookies that can only be decoded using the escape function:

document.cookie = 'escaped=%u5317'
document.cookie = 'default=%E5%8C%97'
var cookies = Cookies.withConverter({
  read: function (value, name) {
    if (name === 'escaped') {
      return unescape(value)
    }
    // Fall back to default for all other cookies
    return Cookies.converter.read(value, name)
  }
})
cookies.get('escaped') // εŒ—
cookies.get('default') // εŒ—
cookies.get() // { escaped: 'εŒ—', default: 'εŒ—' }

Write

Create a new instance of the api that overrides the default encoding implementation:

Cookies.withConverter({
  write: function (value, name) {
    return value.toUpperCase()
  }
})

TypeScript declarations

npm i @types/js-cookie

Server-side integration

Check out the Servers Docs

Contributing

Check out the Contributing Guidelines

Security

For vulnerability reports, send an e-mail to js-cookie at googlegroups dot com

Releasing

Releasing should be done via the Release GitHub Actions workflow, so that published packages on npmjs.com have package provenance.

GitHub releases are created as a draft and need to be published manually! (This is so we are able to craft suitable release notes before publishing.)

Supporters

Many thanks to BrowserStack for providing unlimited browser testing free of cost.

Authors