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 learnAnchor 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
RequirementsAnchor link to section titled "Requirements"
You’ve built a product page.
Step 1: Create a cart routeAnchor link to section titled "Step 1: 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
For now, the code renders an empty cart page. Open http://localhost:3000/cart in your browser to verify.
Add the cart API functionsAnchor link to section titled "Add the cart API functions"
At the bottom of your
cart.jsx page, add the following code:
This code includes the following functions:
cartCreatecreates a cart with line item data using the
cartAddadds line items to an existing cart using the
cartRemoveremoves line items from a cart using the
Each function uses the
context.storefront to make GraphQL queries and mutations. Remix handles reloading the cart data.
These functions are called from the
action function. Since a route can have only one
action function, you need a
switch statement to run the function based on the submitted
Update your loader to replace the
// TODO form action line with the following code:
action function is now ready for form submission. These are the expected form inputs:
||The function to run.||
||The buyer's locale as a two-letter country code.||
||A string array of JSON data with
Step 2: Create an add to cart buttonAnchor link to section titled "Step 2: Create an add to cart button"
You're now ready to add products to the cart. Create a new
ProductForm component in
useMatches, useFetcherat the top of your file:
Add the product form component. This component takes a
variantIdprop and adds it to the
lineshidden input field:
ProductFormcomponent below your Shop Pay button:
You should now be able to add products to your cart. You can add some products to your cart, but you won't be able to see the results until you render the cart data on the page.
Step 3: Create cart componentsAnchor link to section titled "Step 3: 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
app/components. This serves as your single file for all cart-based components.
Render the cart line itemsAnchor link to section titled "Render the cart line items"
Add the following code to your
Cart.jsx file. This code includes two components:
CartLineItemstakes the GraphQL response of a
cart.linesobject and flattens the
edgeobjects to a simple array.
LineItemrenders the cart line item's product image, name, variant data, quantity, and price.
Now you can retrieve the cart data. You need a cart query and a loader function for this route. You'll eventually use this query in two places, so create a new file
app/queries and add the following code:
Then set up your loader function:
Then, 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 Anchor link to section titled "Setup the ItemRemoveButton component"
Cart.jsx component file, add the following components:
ItemRemoveButton button should look similar to the
ProductForm button. This component uses a
fetcher.Form submission to
/cart with a
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 actionsAnchor link to section titled "Setup summary and checkout actions"
Next, you'll handle summary data and checkout actions.
Add the following components to your
These components render cart totals and a checkout button.
Import the components into your
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.
Layout.jsx, import the component and its hooks:
Layout() function, set up the
Below the closing
</main> tag, render your
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
Reload your page to confirm the drawer renders, and then remove this code block.
Step 4: Add a cart icon to the headerAnchor link to section titled "Step 4: Add a cart icon to the header"
In this step, you'll setup a cart icon in your header that opens the drawer.
root.jsx, import the
CART_QUERYthat you built for the cart page, and import the Remix
Create a function to load cart data:
Update the loader to fetch information about the cart:
Suspenseand create a
Layout.jsx, render the new
Now you can open and close the drawer from the header. The drawer will be empty until you render cart components in the drawer.
Step 5: Render cart components in the drawerAnchor link to section titled "Step 5: 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
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 uses different wrapping elements.
Render this component inside your
Your cart drawer should now render up-to-date product data as you add and remove products from your cart.
Step 6: Open the drawer on product addAnchor link to section titled "Step 6: 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 called with the
Layout.jsx, import the necessary dependencies:
Then set up the
It's possible to have several fetchers querying or submitting data at once. You need to filter down the active fetches to the "Add to cart" actions. When an "Add to cart" action is found, open the drawer inside a
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
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.