Skip to main content

Set up a cart handler

Note

This guide might not be compatible with features introduced in Hydrogen version 2025-05 and above. Check the latest documentation if you encounter any issues.

This guide shows you how to set up a cart handler inside your Hydrogen app. This makes a dynamic cart object available on all requests, so it can be read and updated by Remix actions and loaders.

Tip

These tasks are only required if you're upgrading from a version prior to 2024.7.4. As of version 2024.7.4, the Hydrogen project template includes the cart handler by default.



Anchor to Step 1: Create a ,[object Object], instance with ,[object Object]Step 1: Create a cart instance with createHydrogenContext

Hydrogen uses createHydrogenContext to setup a common set of contexts, which includes cart context, that you will need in your Remix loaders and actions.

Create a context.(js|ts) file. This file should manage all the context that you want to show up in the Remix context.

File

/app/lib/context.ts

import {createHydrogenContext} from '@shopify/hydrogen';
import {AppSession} from '~/lib/session';
import {CART_QUERY_FRAGMENT} from '~/lib/fragments';

/**
* The context implementation is separate from server.ts
* so that type can be extracted for AppLoadContext
* */
export async function createAppLoadContext(
request,
env,
executionContext,
) {
/**
* Open a cache instance in the worker and a custom session instance.
*/
if (!env?.SESSION_SECRET) {
throw new Error('SESSION_SECRET environment variable is not set');
}

const waitUntil = executionContext.waitUntil.bind(executionContext);
const [cache, session] = await Promise.all([
caches.open('hydrogen'),
AppSession.init(request, [env.SESSION_SECRET]),
]);

const hydrogenContext = createHydrogenContext({
env,
request,
cache,
waitUntil,
session,
i18n: {language: 'EN', country: 'US'},
cart: {
queryFragment: CART_QUERY_FRAGMENT,
},
});

return {
...hydrogenContext,
// declare additional Remix loader context
};
}
import {createHydrogenContext} from '@shopify/hydrogen';
import {AppSession} from '~/lib/session';
import {CART_QUERY_FRAGMENT} from '~/lib/fragments';

/**
* The context implementation is separate from server.ts
* so that type can be extracted for AppLoadContext
* */
export async function createAppLoadContext(
request: Request,
env: Env,
executionContext: ExecutionContext,
) {
/**
* Open a cache instance in the worker and a custom session instance.
*/
if (!env?.SESSION_SECRET) {
throw new Error('SESSION_SECRET environment variable is not set');
}

const waitUntil = executionContext.waitUntil.bind(executionContext);
const [cache, session] = await Promise.all([
caches.open('hydrogen'),
AppSession.init(request, [env.SESSION_SECRET]),
]);

const hydrogenContext = createHydrogenContext({
env,
request,
cache,
waitUntil,
session,
i18n: {language: 'EN', country: 'US'},
cart: {
queryFragment: CART_QUERY_FRAGMENT,
},
});

return {
...hydrogenContext,
// declare additional Remix loader context
};
}

Anchor to Step 2: Include the app context in the ,[object Object]Step 2: Include the app context in the server.(js|ts)

In your server.(js|ts) file, include your appLoadContext inside the Remix request handler.

File

server.js

import {createRequestHandler} from '@shopify/remix-oxygen';
import {createAppLoadContext} from '~/lib/context';

/**
* Export a fetch handler in module format.
*/
export default {
async fetch(
request,
env,
executionContext
) {
try {
const appLoadContext = await createAppLoadContext(
request,
env,
executionContext,
);

/**
* Create a Remix request handler and pass
* Hydrogen's Storefront client to the loader context.
*/
const handleRequest = createRequestHandler({
build: remixBuild,
mode: process.env.NODE_ENV,
getLoadContext: () => appLoadContext,
});

...
import {createRequestHandler} from '@shopify/remix-oxygen';
import {createAppLoadContext} from '~/lib/context';

/**
* Export a fetch handler in module format.
*/
export default {
async fetch(
request: Request,
env: Env,
executionContext: ExecutionContext,
): Promise<Response> {
try {
const appLoadContext = await createAppLoadContext(
request,
env,
executionContext,
);

/**
* Create a Remix request handler and pass in the
* appLoadContext to the Remix's loader context.
*/
const handleRequest = createRequestHandler({
build: remixBuild,
mode: process.env.NODE_ENV,
getLoadContext: () => appLoadContext,
});

...

If you're using Typescript, then add the return type of createAppLoadContext type to env.d.ts to have a type association.

TypeScript

env.d.ts
import type {createAppLoadContext} from '~/lib/context';

declare module '@shopify/remix-oxygen' {
interface AppLoadContext
extends Awaited<ReturnType<typeof createAppLoadContext>> {
// to change context type, change the return of createAppLoadContext() instead
}
}

Anchor to Step 3: Decide how you want to manage the cart IDStep 3: Decide how you want to manage the cart ID

Hydrogen provides the following default methods for getting and setting a cart ID:

  • cartGetIdDefault(requestHeaders: Headers): Retrieves the cart ID from the request header cookie. The cookie name must be cart.

  • cartSetIdDefault(): Returns a function that accepts a new cart ID and sets a cookie called cart=<new_cart_id> in the returned Headers object. By default, cartSetIdDefault() sets a session cookie. This means the cookie disappears when the user closes the browser.

    Update cookie settings by passing an options object to the cartSetIdDefault function. This options object accepts the same attribute types provided by the cookie-parsing interface from the Worktop package dependency.

File

/app/lib/context.js

import {cartGetIdDefault, cartSetIdDefault} from '@shopify/hydrogen';
import {CART_QUERY_FRAGMENT} from '~/lib/fragments';

...

const hydrogenContext = createHydrogenContext({
env,
request,
cache,
waitUntil,
session,
i18n: {language: 'EN', country: 'US'},
cart: {
queryFragment: CART_QUERY_FRAGMENT,
getId: cartGetIdDefault(request.headers),
setId: cartSetIdDefault({
maxage: 60 * 60 * 24 * 365, // One year expiry
}),
},
});
import {cartGetIdDefault, cartSetIdDefault} from '@shopify/hydrogen';
import {CART_QUERY_FRAGMENT} from '~/lib/fragments';

...

const hydrogenContext = createHydrogenContext({
env,
request,
cache,
waitUntil,
session,
i18n: {language: 'EN', country: 'US'},
cart: {
queryFragment: CART_QUERY_FRAGMENT,
getId: cartGetIdDefault(request.headers),
setId: cartSetIdDefault({
maxage: 60 * 60 * 24 * 365, // One year expiry
}),
},
});
Note

The default methods for getting and saving a cart use the cookie name cart. This name allows a cart instance to be shared between Liquid and Hydrogen. If you provide custom cookie persistence methods and need to maintain compatibility with a Liquid-coded online store, then make sure to also use the cookie name cart.



Was this page helpful?