All hooks / Storage and URL
useCookie
A cookie's value, a setter, and a remover. Unlike browser-only state, a cookie also reaches the server with the request: pass the request's value as serverValue, and the server render, hydration, and the browser all agree from the first frame.
Re-renders when the cookie changes through this hook (in any component), through the Cookie Store API's change event where the browser supports it, and when the window regains focus, which catches changes made by the server or another tab elsewhere. Values are strings, URL-encoded on write.
Import
import { useCookie } from '@vincent.ri/react-tils'
// or only this hook
import { useCookie } from '@vincent.ri/react-tils/useCookie'Usage
// Server component: read the cookie from the request and pass it down.
const theme = (await cookies()).get('theme')?.value ?? null
return <ThemeToggle serverTheme={theme} />
// Client component
function ThemeToggle({ serverTheme }: { serverTheme: string | null }) {
const [theme, setTheme] = useCookie('theme', serverTheme)
const next = theme === 'dark' ? 'light' : 'dark'
return <button onClick={() => setTheme(next, { maxAgeSeconds: 60 * 60 * 24 * 365 })}>{next}</button>
}Live demo
Set it, then reload: the cookie is still there, and sent to the server too.
When to use it
A preference the server must also know on the first request, so it can render the right thing: theme, locale, currency, a dismissed banner, or an A/B test bucket.
When not to
The value is sensitive (session tokens); set those as HttpOnly cookies on the server, which JavaScript can't read at all. For client-only state, useLocalStorage holds more and doesn't ride along on every request.
API
function useCookie(name: string, serverValue?: string | null): [string | null, (value: string, options?: CookieOptions) => void, (options?: CookieOptions) => void]| Parameter | Description |
|---|---|
name | The cookie name. |
serverValue | The value on the server and during hydration: read it from the request (e.g. (await cookies()).get('theme')?.value in Next.js). Default null. |
Returns [value, set, remove]. set(value, options?) writes the cookie; remove(options?) deletes it (pass the same path and domain it was set with).
Types
export type CookieOptions = {
/** Lifetime in seconds. Default: until the browser session ends. */
maxAgeSeconds?: number;
/** Default `'/'`, so the whole site sees it. */
path?: string;
domain?: string;
/** Default `'lax'`. */
sameSite?: "strict" | "lax" | "none";
/** Send only over HTTPS. Default: `true` on HTTPS pages. Required with `sameSite: 'none'`. */
secure?: boolean;
};Browser support
| Uses | Chrome | Edge | Firefox | Safari | Chrome Android | Safari iOS |
|---|---|---|---|---|---|---|
| document.cookieWidely available | 1+ | 12+ | 68+ | 4+ | 18+ | 3.2+ |
| Cookie Store APIoptionalNewly available | 87+ | 87+ | 140+ | 18.4+ | 87+ | 18.4+ |
- Changes made outside the hook are noticed right away where the Cookie Store API exists, and otherwise when the window regains focus.
Optional features are used when present; without them the hook still works.
First version with support, from MDN's compatibility data (web-features 3.42.0, @mdn/browser-compat-data 8.1.5).
Coming from another library?
Other libraries call this useCookies (react-cookie), useCookieState.