---
title: Set up a cart handler
description: >-
  Learn how to set up a cart handler to be accessible from Remix's actions and
  loaders.
source_url:
  html: 'https://shopify.dev/docs/storefronts/headless/legacy/hydrogen/cart/setup'
  md: 'https://shopify.dev/docs/storefronts/headless/legacy/hydrogen/cart/setup.md'
api_name: hydrogen
---

Legacy Hydrogen

These pages refer to Hydrogen versions 2026-04 and earlier. For versions 2026-10 and later, refer to the latest [Hydrogen docs](https://shopify.dev/docs/storefronts/hydrogen). If you're not using the latest version of Hydrogen, then we recommend that you migrate your storefront. To learn more, refer to [Migrate to the latest version of Hydrogen](https://shopify.dev/docs/storefronts/hydrogen/migrate).

# Set up a cart handler

**Note:**

This guide might not be compatible with features introduced in Hydrogen version 2025-05 and above. Check the [2026-04 reference](https://shopify.dev/docs/api/hydrogen/2026-04) 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.

***

## Requirements

* You've completed the [quickstart guide](https://shopify.dev/docs/storefronts/headless/legacy/hydrogen/getting-started).

***

## Step 1: Create a `cart` instance with `createHydrogenContext`

Hydrogen uses [`createHydrogenContext`](https://shopify.dev/docs/api/hydrogen/2026-04/utilities/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

```jsx
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

```jsx
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
  };
}
```

***

## 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

##### JavaScript

```jsx
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

```jsx
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

```tsx
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
  }
}
```

***

## Step 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](https://github.com/lukeed/worktop/blob/master/src/cookie.d.ts) provided by the cookie-parsing interface from the Worktop package dependency.

## File

## /app/lib/context.js

##### JavaScript

```jsx
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

```jsx
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`.

***

## Next steps

* Learn how to [read data from a cart](https://shopify.dev/docs/storefronts/headless/legacy/hydrogen/cart/read)
* Learn how to [customize your own cart handler](https://shopify.dev/docs/storefronts/headless/legacy/hydrogen/cart/customize-cart-handler).

***
