Managing the cart

This is for:

Developer

When building your commerce interfaces, it’s crucial to manage the state of the cart using Headless.

Use Headless to send the cart state with every Commerce API request, ensuring actions affecting the cart—such as adding or removing a product, or purchasing the cart’s contents—emit the correct cart and purchase events.

Managing the cart with Headless involves the following steps:

Initialize the commerce engine with the cart state

When initializing the commerce engine, you can pass the initial state of the cart by setting the cart object in the engine configuration.

import { buildCommerceEngine } from '@coveo/headless/commerce';
import { loadCartItemsFromLocalStorage } from '../utils/cart-utils';

export const getEngine = () => {
  if (_engine !== null) {
    return _engine;
  }

  _engine = buildCommerceEngine({
    configuration: {
      organizationId: '<ORGANIZATION_ID>',
      accessToken: '<ACCESS_TOKEN>',
      analytics: {
        trackingId: '<TRACKING_ID>'
      },
      context: {
        currency: '<CURRENCY>',
        country: '<COUNTRY>',
        language: '<LANGUAGE>',
        view: {
          url: '<URL>'
        },
      }
      cart: {
        items: loadCartItemsFromLocalStorage() ?? [], 1
      },
    },
  });

  return _engine;
};
1 Pass in the initial state of the cart by specifying the CartInitialState object. For your project, you can retrieve this information based on your specific cart management solution.

In this code sample, a custom loadCartItemsFromLocalStorage function is used to initialize the cart from local storage. To learn more about this function, see the following section.

Saving the cart state

It’s important to save the cart state whenever it’s modified so that it can be restored when the commerce engine is initialized. This is especially useful in single-page applications (SPAs), where the cart state might be lost when the page is refreshed, and in multi-page applications (MPAs), where the cart state can be lost when users navigate to different pages.

One option is to save the cart state to local storage.

The following code snippets demonstrate utility functions for saving and loading the cart state to and from local storage.

import { CartItemWithMetadata, CartState } from '@coveo/headless/commerce';

export function saveCartItemsToLocaleStorage(cartState: CartState): void {
  try {
    window.localStorage.setItem(
      'coveo-cartState',
      JSON.stringify(cartState.items)
    );
  } catch (err) {
    console.error('Failed to save cart items to local storage', err);
  }
}

export function loadCartItemsFromLocalStorage(): CartItemWithMetadata[] | null {
  try {
    const cartItems = window.localStorage.getItem('coveo-cartState');
    return cartItems ? JSON.parse(cartItems) : null;
  } catch (err) {
    console.error('Failed to load cart items from local storage', err);
    return null;
  }
}

For more details on how an engine is initialized in a sample project, see the example in the Headless repository.

Modify the cart state on user interaction

Whenever the user modifies the state of the cart by changing its contents, you must update the cart state using the Cart controller. Changing the contents of the cart can include actions such as adding or removing a product, adjusting the quantity of a product, or purchasing the cart’s contents.

Ensure that the cart state is saved whenever it’s modified so it can be restored upon initializing the commerce engine.

The following code snippet shows an example of a React component that manages the cart state using the Cart controller.

import { CartItem, Cart as HeadlessCart } from '@coveo/headless/commerce';
import { useEffect, useState } from 'react';
import { saveCartItemsToLocaleStorage } from '../../utils/cart-utils';

interface ICartProps {
  controller: HeadlessCart;
}

export default function Cart(props: ICartProps) {
  const {controller} = props;

  const [state, setState] = useState(controller.state);

  useEffect(() => {
    controller.subscribe(() => { 1
      setState(controller.state);
      saveCartItemsToLocaleStorage(controller.state);
    });
  }, [controller]);

  const adjustQuantity = (item: CartItem, delta: number) => { 2
    controller.updateItemQuantity({
      ...item,
      quantity: item.quantity + delta,
    });
  };

  const isCartEmpty = () => {
    return state.items.length === 0;
  };

  const purchase = () => { 3
    controller.purchase({id: crypto.randomUUID(), revenue: state.totalPrice});
  };

  const emptyCart = () => { 4
    controller.empty();
  };

  return (
    <div className="Cart">
      <ul>
        {state.items.map((item, index) => ( 5
          <li key={index}>
            <p>
              <span>Name: </span>
              <span>{item.name}</span>
            </p>
            <p>
              <span>Quantity: </span>
              <span>{item.quantity}</span>
            </p>
            <p>
              <span>Price: </span>
              <span>{item.price}</span>
            </p>
            <p>
              <span>Total: </span>
              <span>{item.price * item.quantity}</span>
            </p>
            <button onClick={() => adjustQuantity(item, 1)}>Add one</button>
            <button onClick={() => adjustQuantity(item, -1)}>Remove one</button>
            <button onClick={() => adjustQuantity(item, -item.quantity)}>
              Remove all
            </button>
          </li>
        ))}
      </ul>
      <p>
        <span>Total: </span>
          state.totalPrice
        <span></span>
      </p>
      <button disabled={isCartEmpty()} onClick={purchase}>
        Purchase
      </button>
      <button disabled={isCartEmpty()} onClick={emptyCart}>
        Empty cart
      </button>
    </div>
  );
}
1 Subscribe to the cart state changes. This allows you to update the component state and save the cart state to local storage every time the state is modified.
2 Create a function to adjust the quantity of a cart item by calling the updateItemQuantity method on the Cart controller and passing in the updated quantity.

Calling the`updateItemQuantity` method on the cart controller emits a cart event.

3 Create a function to purchase the cart items by calling the purchase method on the Cart controller. Here, a unique ID is generated for the purchase and the total revenue is set to the total price of the cart.

Calling the purchase method on the cart controller emits a purchase event. You may have a different method for determining your purchase ID depending on your specific implementation.

4 Implement a function that empties the content of the cart by calling the empty method on the controller.

Calling the empty method on the cart controller emits additional cart events.

5 Display the cart items and provide buttons to adjust the quantity of each item using the previously defined functions.

The component above illustrates a possible design for the cart page in a commerce application. For more details on how this component is used in a sample project, see the example in the Headless repository.

Users can also interact with the cart from other pages, such as a Product listing page (PLP), where they can add products directly to the cart. In these cases, use the appropriate Cart controller methods to ensure analytics events are emitted correctly.