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