Skip to main content

createSessionStorage

Creates a SessionStorage object using a custom storage strategy. This is a low-level API for building session storage with your own backend (database, Redis, etc.).

Signature

function createSessionStorage<Data = SessionData, FlashData = Data>(
  strategy: SessionIdStorageStrategy<Data, FlashData>
): SessionStorage<Data, FlashData>
strategy
SessionIdStorageStrategy
required
A storage strategy object that defines how session data is stored and retrieved.
The cookie used to store the session ID, or options to create one automatically. Defaults to a cookie named "__session".
createData
function
required
Creates a new session record and returns the session ID.
createData(
  data: SessionData,
  expires?: Date
): Promise<string>
readData
function
required
Reads session data for the given session ID. Returns null if not found.
readData(id: string): Promise<SessionData | null>
updateData
function
required
Updates session data for the given session ID.
updateData(
  id: string,
  data: SessionData,
  expires?: Date
): Promise<void>
deleteData
function
required
Deletes session data for the given session ID.
deleteData(id: string): Promise<void>

Returns

SessionStorage
object
A session storage object with methods to manage sessions.
getSession
function
Parses the session ID from the Cookie header and loads the session data.
getSession(
  cookieHeader?: string | null,
  options?: ParseOptions
): Promise<Session>
commitSession
function
Saves session data and returns the Set-Cookie header.
commitSession(
  session: Session,
  options?: SerializeOptions
): Promise<string>
destroySession
function
Deletes session data and returns a Set-Cookie header that clears the cookie.
destroySession(
  session: Session,
  options?: SerializeOptions
): Promise<string>

Database Session Storage Example

Create session storage backed by a PostgreSQL database:
filename=app/sessions.server.ts
import { createSessionStorage } from "react-router";
import { db } from "./db.server";

type SessionData = {
  userId: string;
};

export const sessionStorage = createSessionStorage<SessionData>({
  cookie: {
    name: "__session",
    secrets: [process.env.SESSION_SECRET],
    sameSite: "lax",
    path: "/",
    httpOnly: true,
    secure: process.env.NODE_ENV === "production",
  },
  
  async createData(data, expires) {
    // Generate a unique session ID
    const id = crypto.randomUUID();
    
    // Store in database
    await db.session.create({
      data: {
        id,
        data: JSON.stringify(data),
        expiresAt: expires,
      },
    });
    
    return id;
  },
  
  async readData(id) {
    // Fetch from database
    const session = await db.session.findUnique({
      where: { id },
    });
    
    if (!session) return null;
    
    // Check expiration
    if (session.expiresAt && session.expiresAt < new Date()) {
      // Clean up expired session
      await db.session.delete({ where: { id } });
      return null;
    }
    
    return JSON.parse(session.data);
  },
  
  async updateData(id, data, expires) {
    await db.session.update({
      where: { id },
      data: {
        data: JSON.stringify(data),
        expiresAt: expires,
      },
    });
  },
  
  async deleteData(id) {
    await db.session.delete({ where: { id } });
  },
});

Redis Session Storage Example

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

export const sessionStorage = createSessionStorage({
  cookie: {
    name: "__session",
    secrets: [process.env.SESSION_SECRET],
    maxAge: 60 * 60 * 24 * 7, // 7 days
  },
  
  async createData(data, expires) {
    const id = crypto.randomUUID();
    const ttl = expires 
      ? Math.floor((expires.getTime() - Date.now()) / 1000)
      : 60 * 60 * 24 * 7; // 7 days default
    
    await redis.setex(
      `session:${id}`,
      ttl,
      JSON.stringify(data)
    );
    
    return id;
  },
  
  async readData(id) {
    const data = await redis.get(`session:${id}`);
    return data ? JSON.parse(data) : null;
  },
  
  async updateData(id, data, expires) {
    const ttl = expires
      ? Math.floor((expires.getTime() - Date.now()) / 1000)
      : 60 * 60 * 24 * 7;
    
    await redis.setex(
      `session:${id}`,
      ttl,
      JSON.stringify(data)
    );
  },
  
  async deleteData(id) {
    await redis.del(`session:${id}`);
  },
});

Using Session Storage

Once created, use the session storage in your routes:
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 userId = await authenticateUser(formData);
  
  // Get or create session
  const session = await sessionStorage.getSession(
    request.headers.get("Cookie")
  );
  
  // Set session data
  session.set("userId", userId);
  
  // Commit session and redirect
  return redirect("/dashboard", {
    headers: {
      "Set-Cookie": await sessionStorage.commitSession(session),
    },
  });
}

Reading Session Data

filename=app/routes/dashboard.tsx
import { redirect } from "react-router";
import type { Route } from "./+types/dashboard";
import { sessionStorage } from "~/sessions.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 };
}

Destroying Sessions

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),
    },
  });
}

TypeScript Support

Define session data types for full type safety:
type SessionData = {
  userId: string;
  role: "admin" | "user";
};

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

const sessionStorage = createSessionStorage<SessionData, FlashData>({
  cookie: { name: "__session", secrets: ["secret"] },
  async createData(data, expires) {
    // data is typed as SessionData
    return crypto.randomUUID();
  },
  // ... other methods
});

// In your route
const session = await sessionStorage.getSession();
const userId = session.get("userId"); // string
const role = session.get("role");     // "admin" | "user"

Security Considerations

Always Sign Session Cookies

Use secrets to prevent session hijacking:
const sessionStorage = createSessionStorage({
  cookie: {
    name: "__session",
    secrets: [process.env.SESSION_SECRET], // Required!
    httpOnly: true,
    secure: process.env.NODE_ENV === "production",
    sameSite: "lax",
  },
  // ... storage strategy
});

Implement Session Expiration

Always set and check expiration dates:
async readData(id) {
  const session = await db.session.findUnique({ where: { id } });
  
  // Always check expiration
  if (session?.expiresAt && session.expiresAt < new Date()) {
    await this.deleteData(id);
    return null;
  }
  
  return session?.data || null;
}

Generate Secure Session IDs

Use cryptographically secure random IDs:
// Good - cryptographically secure
async createData(data) {
  const id = crypto.randomUUID();
  // ... store data
  return id;
}

// Bad - predictable IDs are a security risk
async createData(data) {
  const id = Math.random().toString(36); // Don't do this!
  return id;
}

Clean Up Expired Sessions

Implement periodic cleanup to prevent storage bloat:
// Add a background job to clean expired sessions
setInterval(async () => {
  await db.session.deleteMany({
    where: {
      expiresAt: { lt: new Date() },
    },
  });
}, 60 * 60 * 1000); // Every hour

Build docs developers (and LLMs) love