Lab - Build a Basic Cart Workflow
Lab - Build a Basic Cart Workflow
Lab - Build a Basic Cart Workflow
This exercise will expand on your basic Next.js storefront with the capability to add products to a cart and check out.
This exercise builds on the basic Next.js application begun in previous labs and has the same technical requirements.
Your store should have the following catalog data associated with your headless channel:
The simple cart workflow you’ll build in the exercise will not support products that require option selections.
This exercise continues where you left off previously in your basic Next.js project. Simply start the dev server from the project root if it’s not currently running:
If you need a fresh start, you can follow the instructions below to set up a new project complete with previous exercise code.
.env.example as .env.local and modify .env.local with configuration details for your store.pnpm output and verify your catalog pages load successfully.You may choose to download or clone the completed version of this lab in a separate location to serve as a reference for the final state.
The objective of this section is to enable customers to add a product to a cart from the product detail page. This will require not one but two GraphQL mutations (for creating a new cart or adding an item to an existing one), as well as tracking the user’s cart ID using a cookie.
We’ll start with the button itself.
components/product/add-to-cart.tsx. and update the component as follows.Note that the new product prop accepted by the component is expected to match the interface previously defined.
A very simple “loading” state in the component will give the user feedback while the request executes.
Finally, notice the directive 'use client' located at the top of the file. Because this component involves interactivity (responding to a button click), this directive declares it as a client component, with the JSX and logic being executed in the browser.
onClick, which will set the loading state, call the appropriate function, then un-set the loading state.Despite being browser-executed JavaScript, this event handler calls the function addProductToCart directly to execute the proper GraphQL request.
In the file where this function is defined - components/product/_actions/add-product-to-cart.ts - the directive 'use server' declares it as a Server Function. When executed by the browser, the result is actually a network request, with addProductToCart being run at the server. This is a convenient pattern for running server-side logic from the client without the need for manually creating API routes.
We’re not filling in the logic for addProductToCart yet, but it currently doesn’t accept the productId param being passed to it.
components/product/_actions/add-product-to-cart.ts and modify the function as follows.app/product/[...productPath]/page.tsx, add the appropriate component placement.types/cart.ts and fill in the definitions of BasicCart and CartFragment.In this case, you’ve included a GraphQL fragment because this fragment will be needed by mutations in two separate files.
components/product/_actions/add-product-to-cart.ts and add the GraphQL and vars/response types for creating a new cart.createCart function to perform the mutation and return the resulting cart data.The createCart function will be appropriate for when the user has no pre-existing cart. You’ll need a separate function, similar in most respects, for a different mutation when a cart exists.
You can see that the structure is nearly identical to that of createCart. The chief distinction is the inclusion of a cartId argument for the mutation.
addCartLineItem to perform the mutation.Finally, you’ll need the existing addProductToCart function to act as the main controller for this operation, deciding on whether to create a cart or add to an existing one.
addProductToCart function as follows.The above code is doing several important things:
lib/cookies.ts are being used to appropriately prefix the cookie name based on whether HTTPS is being used.addCartLineItem mutation is tried if the user has a “cartId” cookie.cartId cookie was set or the attempt to add an item failed, the createCart mutation is used to generate a new cart with the item.cartId cookie.cartId cookie.The storefront not only displays no status update indicating an item has been added to the cart, but also provides no navigation to the cart itself. Let’s implement a mini-cart in the site header to meet both objectives.
The mini-cart will require fetching data matching the user’s cart ID using another GraphQL query, and this data will be required on every page of the storefront. While you might consider including this cart data in the same fetch populating the rest of the header, these details are both user-specific and non-essential for the main content on any given page. Decoupling this into its own component will give you more flexibility in the future for strategies such as caching or partial prerendering.
components/mini-cart/index.tsx and update the component as follows.The component does the work of checking for a cart ID from the appropriate cookie and passing this to an appropriate data fetching function.
components/header/index.tsx, add the MiniCart component.The new mini-cart will display a simple cart icon if there is no current cart; the total quantity and grand total will be added to this if cart data is loaded.
Now let’s set up the data fetching.
components/mini-cart/_data/component-data.ts. The logic for the getCart query will look similar to that of the createCart and addCartLineItem mutations, just without the need for any arguments. Add the GraphQL query and type definitions.getCart function to perform the query.The header mini-cart already links to the path /cart, which is currently a blank page.
The information to be displayed on the cart page includes more details than in the header - namely, the cart line items. These further details are handled with a separate query.
types/cart.ts, fill in the details of the CartItem and BasicCartDetails interfaces.app/cart/page-data.ts and add the query and type definitions.Note that separate GraphQL types must be queried for physicalItems and digitalItems, but the same fields are queried for both.
getCartDetails function to perform the query.app/cart/page.tsx, where we’ll start with the same initial logging step we’ve done for previous pages. Modify the page component to fetch and log the cart details.components/cart/item-row.tsx. This component will be used to display an individual line item on the cart page. Update the component definition as follows.app/cart/page.tsx and update the component logic to render a simple “no items” message if no cart was loaded. We’ll also set up the currency formatter that will be needed for displaying the cart total. (Don’t forget to remove the previous logging statement.)CartPage component with the appropriate content.For completion of checkout, your storefront will redirect users to the BigCommerce storefront using the checkout URL set on your Channel Site. (Unless you have updated it, this will be the URL of your store’s default channel.)
app/cart/page-data.ts and add the query and type definitions.createCartRedirect.app/cart/page.tsx, update the component logic to fetch and return the checkout redirect URL.CartPage component to add a “Proceed to Checkout” link in the JSX.The base URL used to generate the cart redirect URLs depends on the checkout URL set on the Channel Site. In production, it is important to make sure you have correctly configured this URL for the channel associated with your headless storefront using the Update a Channel Site API endpoint, or you will not get correct redirect URLs.
Remember that, for these lab exercises, we have left the default checkout URL in place, meaning the shopper arrives at the store’s default Stencil storefront for checkout.
The configured checkout URL will affect the experience of getting back to the main storefront. You may notice that after arriving on the order confirmation page in your sandbox environment, the links on the page (such as the main store logo) navigate to your default Stencil storefront.
In production with a single-channel setup and properly configured domains, all navigation links on the order confirmation page should properly take the customer back to the headless storefront. As a reminder, you should update both the primary and checkout URLs on the Channel Site for this behavior:
mystore.com). With this in place, the BigCommerce hosted storefront will know what base URL to use for the links generated on the order confirmation page.checkout.mystore.com) and configure DNS for this subdomain to point to your headless channel’s canonical BigCommerce URL.If you do legitimately need to redirect shoppers to a different channel for checkout in production, then additional customization will be necessary to direct them back to the proper storefront after placing an order.
This cart implementation is a good demonstration of the essential workflows involved, but to be truly complete, a few things are still missing. Try the following on your own if you like.
CartItemRow component.__typename in the returned data.) Capture selected options and translate them into the appropriate arguments in the Add to Cart mutations.