Skip to main content

createCookie

Creates a logical container for managing a browser cookie from the server. A Cookie is a container for cookie metadata (name and options) with methods to parse and serialize cookie values.

Signature

function createCookie(
  name: string,
  options?: CookieOptions
): Cookie
name
string
required
The name of the cookie as it appears in the Cookie and Set-Cookie headers.
options
CookieOptions
Configuration options for the cookie.
secrets
string[]
Array of secrets used to sign/unsign cookie values. The first secret is used for signing new cookies. All secrets are tried when parsing to support secret rotation.
domain
string
Specifies the domain for the cookie. Defaults to the current domain.
path
string
default:"/"
Specifies the URL path that must exist in the requested URL for the browser to send the cookie.
maxAge
number
Maximum age of the cookie in seconds. Takes precedence over expires.
expires
Date
Expiration date of the cookie. Use maxAge instead for relative expiration.
httpOnly
boolean
When true, the cookie is inaccessible to JavaScript’s document.cookie API.
secure
boolean
When true, the cookie is only sent over HTTPS connections.
sameSite
'lax' | 'strict' | 'none'
default:"lax"
Controls when the cookie is sent with cross-site requests.
  • "strict" - Cookie is only sent for same-site requests
  • "lax" - Cookie is sent for top-level navigations and same-site requests
  • "none" - Cookie is sent for all requests (requires secure: true)

Returns

A cookie container object with the following properties and methods:
name
string
The name of the cookie.
isSigned
boolean
true if the cookie uses one or more secrets for signing.
expires
Date | undefined
The expiration date of the cookie, calculated from maxAge or expires option.
parse
function
Parses a raw Cookie header and returns the value of this cookie or null if not present.
parse(cookieHeader: string | null, options?: ParseOptions): Promise<any>
serialize
function
Serializes a value and returns the Set-Cookie header string.
serialize(value: any, options?: SerializeOptions): Promise<string>

Basic Example

import { createCookie } from "react-router";

const cookie = createCookie("theme", {
  maxAge: 60 * 60 * 24 * 365, // 1 year
});
Extract cookie values from incoming requests:
filename=app/routes/theme.tsx
import { createCookie } from "react-router";
import type { Route } from "./+types/theme";

const themeCookie = createCookie("theme");

export async function loader({ request }: Route.LoaderArgs) {
  const cookieHeader = request.headers.get("Cookie");
  const theme = await themeCookie.parse(cookieHeader);
  
  return { theme: theme || "light" };
}
Set cookies in responses:
filename=app/routes/set-theme.tsx
import { createCookie } from "react-router";
import type { Route } from "./+types/set-theme";

const themeCookie = createCookie("theme", {
  maxAge: 60 * 60 * 24 * 365,
});

export async function action({ request }: Route.ActionArgs) {
  const formData = await request.formData();
  const theme = formData.get("theme");
  
  return new Response(null, {
    headers: {
      "Set-Cookie": await themeCookie.serialize(theme),
    },
  });
}

Signed Cookies

Sign cookies to prevent tampering:
import { createCookie } from "react-router";

const userIdCookie = createCookie("userId", {
  secrets: ["s3cr3t-n3w", "s3cr3t-old"],
  httpOnly: true,
  secure: true,
  sameSite: "lax",
});

// Parse will verify signature
const userId = await userIdCookie.parse(cookieHeader);
// Returns null if signature is invalid

// Serialize will sign with the first secret
const setCookie = await userIdCookie.serialize("user-123");

Secret Rotation

Rotate secrets without invalidating existing cookies:
const cookie = createCookie("session", {
  secrets: [
    "new-secret-2024",  // Used for signing new cookies
    "old-secret-2023",  // Still validates old cookies
  ],
});

Override Options at Runtime

You can override cookie options when serializing:
const cookie = createCookie("prefs", {
  maxAge: 60 * 60 * 24 * 30, // 30 days default
});

// Extend expiration for premium users
const setCookie = await cookie.serialize(preferences, {
  maxAge: 60 * 60 * 24 * 365, // 1 year
});
Set a cookie with past expiration:
const cookie = createCookie("token");

// Delete the cookie
const setCookie = await cookie.serialize("", {
  maxAge: 0,
});

return new Response(null, {
  headers: { "Set-Cookie": setCookie },
});

Complex Data Types

Cookies automatically serialize and deserialize JSON:
const preferenceCookie = createCookie("preferences");

// Serialize object
await preferenceCookie.serialize({
  theme: "dark",
  language: "en",
  notifications: true,
});

// Parse returns the object
const prefs = await preferenceCookie.parse(cookieHeader);
// { theme: "dark", language: "en", notifications: true }

Security Considerations

Always Sign Sensitive Cookies

Prevent tampering by signing cookies that contain important data:
// Bad - unsigned cookie can be modified by the client
const userCookie = createCookie("userId");

// Good - signed cookie is verified on parse
const userCookie = createCookie("userId", {
  secrets: [process.env.COOKIE_SECRET],
});

Use HttpOnly for Session Cookies

Prevent XSS attacks from accessing sensitive cookies:
const sessionCookie = createCookie("session", {
  httpOnly: true,  // JavaScript cannot access this cookie
  secure: true,    // Only sent over HTTPS
  sameSite: "lax", // CSRF protection
  secrets: [process.env.SESSION_SECRET],
});

Secure in Production

Always enable secure in production to prevent man-in-the-middle attacks:
const cookie = createCookie("data", {
  secure: process.env.NODE_ENV === "production",
});
Browsers limit cookie size to about 4KB. For larger data, use session storage instead:
// Bad - may exceed browser limits
const largeCookie = createCookie("data");
await largeCookie.serialize({ ...largeObject });

// Good - use session storage for large data
import { createCookieSessionStorage } from "react-router";
const sessionStorage = createCookieSessionStorage({
  cookie: { name: "session", secrets: ["secret"] },
});

Build docs developers (and LLMs) love