Build a Cart with Hydrogen
Previously, you built a product page. Your Hydrogen storefront now renders detailed information about products and provides a "Shop Pay" button to customers. You're now ready to build a cart in your app.
In this tutorial, you'll build a cart that contains the merchandise that a customer wants to buy, and the estimated cost that's associated with the cart.
What you’ll learn
Anchor link to section titled "What you’ll learn"In this tutorial, you’ll learn how to do the following tasks:
- Create a cart route with a Remix action to handle submitted form data
- Build cart components that can be used on a page or in a drawer component
- Update the product details page to include an Add to cart option
Requirements
Anchor link to section titled "Requirements"- You’ve built a product page.
- You're using API version 2023-07 or higher.
Step 1: Create a cart
instance with createCartHandler
Anchor link to section titled "Step 1: Create a cart instance with createCartHandler"The createCartHandler
utility returns an object instance for you to interact with Storefront API cart queries. The utility enables you to easily interact with the cart.
In your server.js
file, create your cart handler inside the server fetch handler.
Next, pass your cart
instance to the getLoadContext
function.
If you're using Typescript, then add the HydrogenCart
type to remix.env.d.ts
to have a type association. If you've added custom methods to the createCartHandler
utility, then use the HydrogenCartCustom
type instead.
Step 2: Decide how you want to manage the cart ID
Anchor link to section titled "Step 2: Decide how you want to manage the cart ID"The createCartHandler
utility enables you to determine how and where you store 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 cookiecart=<new_cart_id>
in the returnedHeaders
object. By default,cartSetIdDefault()
sets a session cookie. This means the cookie will disappear when the visitor closes the browser.
Update cookie settings by passing options to cartSetIdDefault
.
Step 3: Create a cart route
Anchor link to section titled "Step 3: Create a cart route"You need a cart instance and cart data to build the UI pieces for your cart. Your first step is to set up a route that handles an "add to cart" form action.
Create a new file /app/routes/cart.jsx
and add the following code:
This code includes a Remix action function that handles a form submission to the /cart
route.
For now, the code should render an empty cart page. To verify this, open http://localhost:3000/cart in your browser.

Your cart action
function is ready to accept form requests for adding, updating, and removing cart items. We'll continue to create the forms that submits to the cart action.
Step 4: Create an add to cart button
Anchor link to section titled "Step 4: Create an add to cart button"You're now ready to add products to the cart. Create a new ProductForm
component in products.$handle.jsx
:
At the top of the file, import
CartForm
:Add the product form component. This component takes a
variantId
prop and adds it to thelines
hidden input field:Render the
ProductForm
component below your Shop Pay button:
You should now be able to add products to your cart. However, you won't be able to see the results until you render the cart data on the page.
Step 5: Create cart components
Anchor link to section titled "Step 5: Create cart components"You should have a cart saved to your session that includes a few products. In this step, you'll render these components on the cart page:
- Cart line items: Displays the product data and enables removal of line items.
- Cart summary: Shows pricing data.
- Cart actions: Renders a "Continue to checkout" button.
You will eventually render these components in two places: the cart page and a drawer component. This step of the guide focuses on the cart page.
Create a new file Cart.jsx
in app/components
. This serves as your single file for all cart-based components.
Render the cart line items
Anchor link to section titled "Render the cart line items"Add the following code to your Cart.jsx
file. This code includes two components:
CartLineItems
takes the GraphQL response of acart.lines
object and flattens thenode
andedge
objects to a simple array.LineItem
renders the cart line item's product image, name, variant data, quantity, and price.
Now you can retrieve the cart data. You need a loader function for this route. You can use the cart handler to get your cart data. If you need to customize the cart data, then you can choose to customize the cart fragment.
Next, update your Cart
function to get the cart
data from the loader. If the cart data doesn't exist or the cart is empty, then render the empty cart block:
Visit http://localhost:3000/cart in your browser. You should be able to see your cart line items.

Setup the ItemRemoveButton
component
Anchor link to section titled "Setup the ItemRemoveButton component"In your Cart.jsx
component file, add the following components:
The ItemRemoveButton
button should look similar to the ProductForm
button. This component uses a fetcher.Form
submission to /cart
with a REMOVE_FROM_CART
action.
Render this component in LineItem
with the lineIds
prop under the quantity line.

Refresh your cart page and test the remove button. You now have control of your cart contents.
Setup summary and checkout actions
Anchor link to section titled "Setup summary and checkout actions"Next, you'll handle summary data and checkout actions.
Add the following components to your Cart.jsx
file:
These components render cart totals and a checkout button.
Import the components into your cart.jsx
file:
Replace your placeholder content <p>TODO Cart Summary</p>
with these components:

Your cart page is now complete. The components that you've built in this step can be reused in the cart drawer that you'll build in the next two steps.
The next two steps focus on the cart drawer, which can be used from any page. To speed up your development, you can use the Headless UI Dialog
component. This enables you to focus on the Remix and cart pieces.
In your terminal, install Headless UI:
Create a new Drawer.jsx
component and paste in the following code:
This code renders a styled Headless UI <Dialog>
element. It also exports a hook with functions to open and close it from any component.
In Layout.jsx
, import the component and its hooks:
In the Layout()
function, set up the useDrawer()
hook:
Below the closing </main>
tag, render your Drawer
:
You don't have a way to see your drawer right now. It renders in the closed state. If you want to test the Drawer, you can run the openDrawer()
function once when the Layout
component mounts. Add this code before the return
statement in app/components/Layout.jsx
:
Reload your page to confirm the drawer renders, and then remove this code block.
Step 6: Add a cart icon to the header
Anchor link to section titled "Step 6: Add a cart icon to the header"In this step, you'll set up a cart icon in your header that opens the drawer.
In
root.jsx
, import the Remixdefer
function:Update the loader to fetch information about the cart:
In
Layout.jsx
, importAwait
andSuspense
and create aCartHeader
component:In
Layout.jsx
, render the newCartHeader
component:

You can now open and close the drawer from the header. The drawer is empty until you render cart components in the drawer.
Step 7: Render cart components in the drawer
Anchor link to section titled "Step 7: Render cart components in the drawer"The cart page and the cart drawer will display the same data. The only difference is the layout.
You can create a new CartDrawer
component in Layout.jsx
.
Import the components at the top of the file:
Add the following code to the bottom of the file:
This uses similar code to the cart components that you created previously. The cart components are rendered with the same data, but use different wrapping elements.
Render this component inside your Drawer
:

Your cart drawer should now render up-to-date product data as you add and remove products from your cart.
Step 8: Open the drawer on product add
Anchor link to section titled "Step 8: Open the drawer on product add"In this step, you'll learn how to open the drawer when a product is added to the cart. Your app can respond to any fetcher function that's defined with the CartForm
.
In Layout.jsx
, import the necessary dependencies:
Then set up the useFetchers
hook:
It's possible to have several fetchers that are querying or submitting data at the same time. You therefore need to filter down the active fetches to the LinesAdd
actions. When an LinesAdd
action is found, open the drawer inside a useEffect
hook:

The cart drawer should now open when an add to cart form is submitted. The cart data automatically refreshes when the action finishes and Remix runs its loaders.
This guide shows how to create a functional cart, but you can progressively enhance it with additional features. The following are some examples:
- Add increment and decrement functionality for each line item
- Add optimistic UI for adding, updating, and removing cart line items
- Change a line item's options
- Calculate shipping
- Add a discount code
The demo store
template includes a fully featured cart. You can explore all of these features and build upon what you have learned in this guide.
You can build on the code in this guide as you explore more features of Hydrogen and Remix.