Set up a cart handler
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 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.
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.
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 RequirementsRequirements
- You've completed the quickstart guide.
Anchor to Step 1: Create a ,[object Object], instance with ,[object Object]Step 1: Create a cart instance with createHydrogenContext
cart instance with createHydrogenContextHydrogen 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
JavaScript
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
};
}TypeScript
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)
server.(js|ts)In your server.(js|ts) file, include your appLoadContext inside the Remix request handler.
File
server.js
JavaScript
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,
});
...TypeScript
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.tsAnchor 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 becart. -
cartSetIdDefault(): Returns a function that accepts a new cart ID and sets a cookie calledcart=<new_cart_id>in the returnedHeadersobject. 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
cartSetIdDefaultfunction. This options object accepts the same attribute types provided by the cookie-parsing interface from the Worktop package dependency.
File
/app/lib/context.js
JavaScript
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
}),
},
});TypeScript
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
}),
},
});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.
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.
Anchor to Next stepsNext steps
- Learn how to read data from a cart
- Learn how to customize your own cart handler.