Skip to main content

createCookieSessionStorage

Creates a SessionStorage object that stores all session data directly in the session cookie itself. This eliminates the need for a server-side database or session store.

Signature

function createCookieSessionStorage<Data = SessionData, FlashData = Data>(
  options?: CookieSessionStorageOptions
): SessionStorage<Data, FlashData>
options
CookieSessionStorageOptions
Configuration options for cookie session storage.
The cookie used to store session data, or options to create one automatically.
name
string
default:"__session"
The name of the cookie.
secrets
string[]
Array of secrets for signing cookies. First secret signs new cookies, all are tried when parsing.
domain
string
Cookie domain.
path
string
default:"/"
Cookie path.
maxAge
number
Maximum age in seconds.
httpOnly
boolean
default:"true"
Makes cookie inaccessible to JavaScript.
secure
boolean
Only send cookie over HTTPS.
sameSite
'lax' | 'strict' | 'none'
default:"lax"
Controls cross-site request behavior.

Returns

SessionStorage
object
A session storage object with methods to manage sessions.
getSession
function
Parses the Cookie header and returns a Session object.
getSession(
  cookieHeader?: string | null,
  options?: ParseOptions
): Promise<Session>
commitSession
function
Serializes session data and returns the Set-Cookie header. Throws an error if the cookie exceeds 4KB.
commitSession(
  session: Session,
  options?: SerializeOptions
): Promise<string>
destroySession
function
Returns a Set-Cookie header that clears the session cookie.
destroySession(
  session: Session,
  options?: SerializeOptions
): Promise<string>

Basic Example

filename=app/sessions.server.ts
import { createCookieSessionStorage } from "react-router";

export const sessionStorage = createCookieSessionStorage({
  cookie: {
    name: "__session",
    secrets: [process.env.SESSION_SECRET],
    sameSite: "lax",
    path: "/",
    httpOnly: true,
    secure: process.env.NODE_ENV === "production",
  },
});

Login Example

Set session data after authentication:
filename=app/routes/login.tsx
import { redirect } from "react-router";
import type { Route } from "./+types/login";
import { sessionStorage } from "~/sessions.server";

export async function action({ request }: Route.ActionArgs) {
  const formData = await request.formData();
  const user = await authenticateUser(
    formData.get("email"),
    formData.get("password")
  );
  
  if (!user) {
    return { error: "Invalid credentials" };
  }
  
  // Create session
  const session = await sessionStorage.getSession();
  session.set("userId", user.id);
  
  // Redirect with session cookie
  return redirect("/dashboard", {
    headers: {
      "Set-Cookie": await sessionStorage.commitSession(session),
    },
  });
}

Reading Session Data

Access session data in loaders:
filename=app/routes/dashboard.tsx
import { redirect } from "react-router";
import type { Route } from "./+types/dashboard";
import { sessionStorage } from "~/sessions.server";
import { db } from "~/db.server";

export async function loader({ request }: Route.LoaderArgs) {
  const session = await sessionStorage.getSession(
    request.headers.get("Cookie")
  );
  
  const userId = session.get("userId");
  
  if (!userId) {
    return redirect("/login");
  }
  
  const user = await db.user.findUnique({
    where: { id: userId },
  });
  
  return { user };
}

Flash Messages

Store temporary messages that are automatically removed after being read:
filename=app/routes/settings.tsx
import { redirect } from "react-router";
import type { Route } from "./+types/settings";
import { sessionStorage } from "~/sessions.server";

export async function action({ request }: Route.ActionArgs) {
  const session = await sessionStorage.getSession(
    request.headers.get("Cookie")
  );
  
  // Set flash message
  session.flash("success", "Settings saved successfully!");
  
  return redirect("/settings", {
    headers: {
      "Set-Cookie": await sessionStorage.commitSession(session),
    },
  });
}

export async function loader({ request }: Route.LoaderArgs) {
  const session = await sessionStorage.getSession(
    request.headers.get("Cookie")
  );
  
  // Get flash message (automatically removed after reading)
  const successMessage = session.get("success");
  
  return {
    successMessage,
  };
}

Logout Example

Destroy the session on logout:
filename=app/routes/logout.tsx
import { redirect } from "react-router";
import type { Route } from "./+types/logout";
import { sessionStorage } from "~/sessions.server";

export async function action({ request }: Route.ActionArgs) {
  const session = await sessionStorage.getSession(
    request.headers.get("Cookie")
  );
  
  return redirect("/", {
    headers: {
      "Set-Cookie": await sessionStorage.destroySession(session),
    },
  });
}

Custom Session Expiration

Set expiration when committing the session:
const session = await sessionStorage.getSession();
session.set("userId", user.id);

return redirect("/dashboard", {
  headers: {
    "Set-Cookie": await sessionStorage.commitSession(session, {
      maxAge: 60 * 60 * 24 * 7, // 7 days
    }),
  },
});

TypeScript Support

Define session data types:
filename=app/sessions.server.ts
import { createCookieSessionStorage } from "react-router";

type SessionData = {
  userId: string;
  role: "admin" | "user";
};

type FlashData = {
  error: string;
  success: string;
};

export const sessionStorage = createCookieSessionStorage<
  SessionData,
  FlashData
>({
  cookie: {
    name: "__session",
    secrets: [process.env.SESSION_SECRET],
  },
});

// Usage with type safety
const session = await sessionStorage.getSession();
const userId = session.get("userId"); // typed as string
const role = session.get("role");     // typed as "admin" | "user"
session.flash("error", "Something went wrong"); // typed

Helper Functions

Create utilities to simplify common session operations:
filename=app/sessions.server.ts
import { redirect } from "react-router";
import { sessionStorage } from "./sessions.server";

export async function requireUser(request: Request) {
  const session = await sessionStorage.getSession(
    request.headers.get("Cookie")
  );
  
  const userId = session.get("userId");
  
  if (!userId) {
    throw redirect("/login");
  }
  
  return userId;
}

export async function createUserSession(
  userId: string,
  redirectTo: string
) {
  const session = await sessionStorage.getSession();
  session.set("userId", userId);
  
  return redirect(redirectTo, {
    headers: {
      "Set-Cookie": await sessionStorage.commitSession(session),
    },
  });
}
Use in routes:
filename=app/routes/protected.tsx
import type { Route } from "./+types/protected";
import { requireUser } from "~/sessions.server";

export async function loader({ request }: Route.LoaderArgs) {
  const userId = await requireUser(request);
  // userId is guaranteed to exist here
  return { userId };
}

Advantages

  • No database required - Simplifies deployment and reduces infrastructure
  • Stateless - Works seamlessly in load-balanced environments
  • Fast - No database queries for session data
  • Simple - Easy to set up and maintain

Limitations

Browsers limit cookies to approximately 4KB. Store minimal data:
// Bad - too much data
session.set("cart", largeCartObject); // May exceed 4KB

// Good - store just the ID
session.set("cartId", "cart-123");
The commitSession method throws an error if the cookie exceeds the size limit:
try {
  const setCookie = await sessionStorage.commitSession(session);
} catch (error) {
  // Error: Cookie length will exceed browser maximum. Length: 5234
}

Data Included in Every Request

Cookie session data is sent with every request:
// Store minimal data
type SessionData = {
  userId: string;        // Good - just an ID
  // user: User;         // Bad - full user object
};

Security Considerations

Always Use Secrets

Sign cookies to prevent tampering:
// Bad - unsigned cookies can be modified
const sessionStorage = createCookieSessionStorage({
  cookie: { name: "__session" },
});

// Good - signed cookies are verified
const sessionStorage = createCookieSessionStorage({
  cookie: {
    name: "__session",
    secrets: [process.env.SESSION_SECRET],
  },
});

Use HttpOnly Cookies

Prevent XSS attacks:
const sessionStorage = createCookieSessionStorage({
  cookie: {
    name: "__session",
    secrets: [process.env.SESSION_SECRET],
    httpOnly: true, // JavaScript cannot access this cookie
  },
});

Enable Secure in Production

Only send cookies over HTTPS:
const sessionStorage = createCookieSessionStorage({
  cookie: {
    name: "__session",
    secrets: [process.env.SESSION_SECRET],
    secure: process.env.NODE_ENV === "production",
  },
});

Use SameSite Protection

Prevent CSRF attacks:
const sessionStorage = createCookieSessionStorage({
  cookie: {
    name: "__session",
    secrets: [process.env.SESSION_SECRET],
    sameSite: "lax", // Recommended for most applications
  },
});

Build docs developers (and LLMs) love