Remix Utils - v9.3.1
    Preparing search index...

    Module Server/Typed Cookie

    Note

    Install using bunx shadcn@latest add @remix-utils/typed-cookie.

    Note

    This depends on @standard-schema/spec, and React Router.

    Cookie objects in Remix allows any type, the typed cookies from Remix Utils lets you use any Standard Schema compatible library to parse the cookie values and ensure they conform to a schema.

    import { createCookie } from "react-router";
    import { createTypedCookie } from "remix-utils/typed-cookie";
    import { z } from "zod"; //or another Standard Schema compatible library

    let cookie = createCookie("returnTo", cookieOptions);
    // I recommend you to always add `nullable` to your schema, if a cookie didn't
    // come with the request Cookie header Remix will return null, and it can be
    // useful to remove it later when clearing the cookie
    let schema = z.string().url().nullable();

    // pass the cookie and the schema
    let typedCookie = createTypedCookie({ cookie, schema });

    // this will be a string and also a URL
    let returnTo = await typedCookie.parse(request.headers.get("Cookie"));

    // this will not pass the schema validation and throw a ZodError
    await typedCookie.serialize("a random string that's not a URL");
    // this will make TS yell because it's not a string, if you ignore it it will
    // throw a ZodError
    await typedCookie.serialize(123);

    You could also use typed cookies with any sessionStorage mechanism from Remix.

    let cookie = createCookie("session", cookieOptions);
    let schema = z.object({ token: z.string().nullish() }).nullable();

    let sessionStorage = createCookieSessionStorage({
    cookie: createTypedCookie({ cookie, schema }),
    });

    // if this works then the correct data is stored in the session
    let session = sessionStorage.getSession(request.headers.get("Cookie"));

    session.unset("token"); // remove a required key from the session

    // this will throw a ZodError because the session is missing the required key
    await sessionStorage.commitSession(session);

    Now Zod will ensure the data you try to save to the session is valid removing any extra field and throwing if you don't set the correct data in the session.

    Important

    The session object is not really typed so doing session.get will not return the correct type, you can do schema.parse(session.data) to get the typed version of the session data.

    You can also use async refinements in your schemas because typed cookies uses parseAsync method from Zod.

    let cookie = createCookie("session", cookieOptions);

    let schema = z
    .object({
    token: z.string().refine(async (token) => {
    let user = await getUserByToken(token);
    return user !== null;
    }, "INVALID_TOKEN"),
    })
    .nullable();

    let sessionTypedCookie = createTypedCookie({ cookie, schema });

    // this will throw if the token stored in the cookie is not valid anymore
    sessionTypedCookie.parse(request.headers.get("Cookie"));

    Finally, to be able to delete a cookie, you can add .nullable() to your schema and serialize it with null as value.

    // Set the value as null and expires as current date - 1 second so the browser expires the cookie
    await typedCookie.serialize(null, { expires: new Date(Date.now() - 1) });

    If you didn't add .nullable() to your schema, you will need to provide a mock value and set the expires date to the past.

    let cookie = createCookie("returnTo", cookieOptions);
    let schema = z.string().url().nullable();

    let typedCookie = createTypedCookie({ cookie, schema });

    await typedCookie.serialize("some fake url to pass schema validation", {
    expires: new Date(Date.now() - 1),
    });

    Classes

    ValidationError

    Interfaces

    TypedCookie

    Functions

    createTypedCookie
    isTypedCookie