---
title: "Using Cart Data with WooGraphQL"
description: "Learn how to retrieve and utilize cart data in your headless WooCommerce application with WPGraphQL for WooCommerce and WPGraphQL, enabling seamless shopping experiences for your customers."
keywords: "WooGraphQL, WPGraphQL, WooCommerce, GraphQL, cart data, headless, shopping experience"
author: "Geoff Taylor"
---

# Using Cart Data

In this section, we will create a "/cart" page that displays a table of the items in the cart. We will use the `UserSessionProvider` created in the last section to pull the cart data using `useSession()`. The table will have four columns: `Product`, `Price`, `Quantity`, and `Total`. We will also create a cart totals table that displays the shipping totals, applied coupons, and the final total. Let's start by implementing the changes to the `useCartMutations` hook also created in the previous section.

## Prerequisites

- Basic knowledge of React and React Router.
- Familiarity with GraphQL and WPGraphQL.
- A setup WPGraphQL/WPGraphQL for WooCommerce backend.
- Read previous sections on [Routing By URI](routing-by-uri.md), [Using Product Data](using-product-data.md), and [Handling User Session and Using Cart Mutations](handing-user-session-and-using-cart-mutations).

## Step 0: Create `graphql.js` file

First, create a `graphql.js` file to store all the GraphQL queries and mutations:

```javascript
import { gql } from '@apollo/client';

export const CustomerContent = gql`
  fragment CustomerContent on Customer {
    id
    sessionToken
    shipping {
      postcode
      state
      city
      country
    }
  }
`;

export const ProductContentSlice = gql`
  fragment ProductContentSlice on Product {
    id
    databaseId
    name
    slug
    type
    image {
      id
      sourceUrl(size: WOOCOMMERCE_THUMBNAIL)
      altText
    }
    ... on SimpleProduct {
      price
      regularPrice
      soldIndividually
    }
    ... on VariableProduct {
      price
      regularPrice
      soldIndividually
    }
  }
`;

export const ProductVariationContentSlice = gql`
  fragment ProductVariationContentSlice on ProductVariation {
    id
    databaseId
    name
    slug
    image {
      id
      sourceUrl(size: WOOCOMMERCE_THUMBNAIL)
      altText
    }
    price
    regularPrice
  }
`;

export const ProductContentFull = gql`
  fragment ProductContentFull on Product {
    id
    databaseId
    slug
    name
    type
    description
    shortDescription(format: RAW)
    image {
      id
      sourceUrl
      altText
    }
    galleryImages {
      nodes {
        id
        sourceUrl(size: WOOCOMMERCE_THUMBNAIL)
        altText
      }
    }
    productTags(first: 20) {
      nodes {
        id
        slug
        name
      }
    }
    attributes {
      nodes {
        id
        attributeId
        ... on LocalProductAttribute {
          name
          options
          variation
        }
        ... on GlobalProductAttribute {
          name
          options
          variation
        }
      }
    }
    ... on SimpleProduct {
      onSale
      stockStatus
      price
      rawPrice: price(format: RAW)
      regularPrice
      salePrice
      stockStatus
      stockQuantity
      soldIndividually
    }
    ... on VariableProduct {
      onSale
      price
      rawPrice: price(format: RAW)
      regularPrice
      salePrice
      stockStatus
      stockQuantity
      soldIndividually
      variations(first: 50) {
        nodes {
          id
          databaseId
          name
          price
          rawPrice: price(format: RAW)
          regularPrice
          salePrice
          onSale
          attributes {
            nodes {
              name
              label
              value
            }
          }
        }
      }
    }
  }
`;

export const VariationContent = gql`
  fragment VariationContent on ProductVariation {
    id
    name
    slug
    price
    regularPrice
    salePrice
    stockStatus
    stockQuantity
    onSale
    image {
      id
      sourceUrl
      altText
    }
  }
`;

export const CartItemContent = gql`
  fragment CartItemContent on CartItem {
    key
    product {
      node {
        ...ProductContentSlice
      }
    }
    variation {
      node {
        ...ProductVariationContentSlice
      }
    }
    quantity
    total
    subtotal
    subtotalTax
    extraData {
      key
      value
    }
  }
`;

export const CartContent = gql`
  fragment CartContent on Cart {
    contents(first: 100) {
      itemCount
      nodes {
        ...CartItemContent
      }
    }
    appliedCoupons {
      code
      discountAmount
      discountTax
    }
    needsShippingAddress
    availableShippingMethods {
      packageDetails
      supportsShippingCalculator
      rates {
        id
        instanceId
        methodId
        label
        cost
      }
    }
    subtotal
    subtotalTax
    shippingTax
    shippingTotal
    total
    totalTax
    feeTax
    feeTotal
    discountTax
    discountTotal
  }
`;

export const GetProduct = gql`
  query GetProduct($id: ID!, $idType: ProductIdTypeEnum) {
    product(id: $id, idType: $idType) {
      ...ProductContentFull
    }
  }
`;

export const GetProductVariation = gql`
  query GetProductVariation($id: ID!) {
    productVariation(id: $id, idType: DATABASE_ID) {
      ...VariationContent
    }
  }
`;

export const GetCart = gql`
  query GetCart($customerId: Int) {
    cart {
      ...CartContent
    }
    customer(customerId: $customerId) {
      ...CustomerContent
    }
  }
`;

export const AddToCart = gql`
  mutation AddToCart($productId: Int!, $variationId: Int, $quantity: Int, $extraData: String) {
    addToCart(
      input: {productId: $productId, variationId: $variationId, quantity: $quantity, extraData: $extraData}
    ) {
      cart {
        ...CartContent
      }
      cartItem {
        ...CartItemContent
      }
    }
  }
`;

export const UpdateCartItemQuantities = gql`
  mutation UpdateCartItemQuantities($items: [CartItemQuantityInput]) {
    updateItemQuantities(input: {items: $items}) {
      cart {
        ...CartContent
      }
      items {
        ...CartItemContent
      }
    }
  }
`;

export const RemoveItemsFromCart = gql`
  mutation RemoveItemsFromCart($keys: [ID], $all: Boolean) {
    removeItemsFromCart(input: {keys: $keys, all: $all}) {
      cart {
        ...CartContent
      }
      cartItems {
        ...CartItemContent
      }
    }
  }
`;

export const ApplyCouponToCart = gql`
  mutation ApplyCouponToCart($code: String!) {
    applyCoupon(input: {code: $code}) {
      cart {
        ...CartContent
      }
    }
  }
`;

export const RemoveCouponFromCart = gql`
  mutation RemoveCouponFromCart($code: String!) {
    removeCoupons(input: {codes: [$code]}) {
      cart {
        ...CartContent
      }
    }
  }
`;

export const RemoveCouponsFromCart = gql`
  mutation RemoveCouponsFromCart($codes: [String!]) {
    removeCoupons(input: {codes: $codes}) {
      cart {
        ...CartContent
      }
    }
  }
`;

export const SetShippingLocale = gql`
  mutation SetShippingLocale($zip: String!, $state: String, $city: String, $country: CountriesEnum) {
    updateCustomer(
      input: {shipping: {postcode: $zip, country: $country, state: $state, city: $city}}
    ) {
      customer {
        ...CustomerContent
      }
    }
  }
`;

export const SetShippingMethod = gql`
  mutation SetShippingMethod($shippingMethod: String!) {
    updateShippingMethod(input: {shippingMethods: [$shippingMethod]}) {
      cart {
        ...CartContent
      }
    }
  }
`;

```

## Step 1: Update useCartMutations.js

Update the `useCartMutations` hook as follows:

```javascript
import { useMutation } from '@apollo/client';
import { useSession } from "./SessionProvider";
import {
  AddToCart,
  UpdateCartItemQuantities,
  RemoveItemsFromCart,
  ApplyCouponToCart,
  RemoveCouponFromCart,
  SetShippingLocale,
  SetShippingMethod,
} from './graphql';

const useCartMutations = (
  productId,
  variationId,
  extraData,
) => {
  // ... rest of useCartMutations code.

  const removeCartItemHelper = () => {
    const item = findInCart(productId, variationId, extraData);

    if (!item) {
      throw new Error('Failed to find item in cart.');
    }

    removeCartItem({ variables: { keys: [item.key] } });
  };

  const updateQuantityHelper = (quantity) => {
    const item = findInCart(productId, variationId, extraData);

    if (!item) {
      throw new Error('Failed to find item in cart.');
    }

    updateQuantity({ variables: { items: [{ key: item.key, quantity }] } });
  };

  const addToCartHelper = (pId, quantity, vId, e) => {
    addToCart({
      variables: {
        productId: pId,
        quantity,
        variationId: vId,
        extraData: e,
      },
    });
  };

  const store = {
    adding,
    updating,
    removing,
    quantityFound,
    mutate,
    addToCart: addToCartHelper,
    removeCartItem: removeCartItemHelper,
    updateQuantity: updateQuantityHelper,
  };

  return store;
};
```

This new code adds more helper functions like `removeCartItemHelper`, `updateQuantityHelper`, `addToCartHelper`, and updates the store to include these functions. With these helpers, we get access to the `add`, `update`, and `remove` cart mutations without the need to provide product details to the hook.

And now let's implement the `useOtherCartMutations` hook:

```javascript
export const useOtherCartMutations = () => {
  const {
    setCart,
    setCustomer,
  } = useSession();

  const [applyCouponToCart, { loading: applyingCoupon }] = useMutation({
    mutation: ApplyCouponToCart,
    onCompleted({ applyCoupon: data }) {
      if (data?.cart) {
        setCart(data.cart as Cart);
      }
    },
  });

  const [removeCouponFromCart, { loading: removingCoupon }] = useMutation({
    mutation: RemoveCouponFromCart,
    onCompleted({ removeCoupons: data }) {
      if (data?.cart) {
        setCart(data.cart as Cart);
      }
    },
  });

  const [setShippingLocale, { loading: savingShippingLocale }] = useMutation({
    mutation: SetShippingLocale,
    onCompleted({ updateCustomer: data }) {
      if (data?.customer) {
        setCustomer(data.customer);
      }
    },
  });

  const [setShippingMethod, { loading: savingShippingMethod }] = useMutation({
    mutation: SetShippingMethod,
    onCompleted({ updateShippingMethod: data }) {
      if (data?.cart) {
        setCart(data.cart as Cart);
      }
    },
  });

  const applyCouponHelper = (code) => applyCouponToCart({ variables: { code } });
  const removeCouponHelper = (code) => removeCouponFromCart({ variables: { code } });
  const setShippingLocaleHelper = (input) => setShippingLocale({
    variables: {
      ...input,
    },
  });

  const setShippingMethodHelper = (shippingMethod) => setShippingMethod({
    variables: { shippingMethod },
  });

  const store = {
    applyingCoupon,
    removingCoupon,
    savingShippingInfo: savingShippingLocale || savingShippingMethod,
    applyCoupon: applyCouponHelper,
    removeCoupon: removeCouponHelper,
    setShippingLocale: setShippingLocaleHelper,
    setShippingMethod: setShippingMethodHelper,
  };

  return store;
};
```

This hook provides the helper callbacks for the other cart mutations. These mutations are the ones that effect the cart and not the cart items, at least not directly, like `applyCoupon`.

## Step 2: Create the `/cart` page

After making the necessary updates to `useCartMutations` and `useOtherCartMutations`, we can now create the `/cart` page. First, import the necessary components and hooks.

```javascript
import React, { useState } from 'react';
import { useSession } from './SessionProvider';
import { useCartMutations, useOtherCartMutations } from './useCartMutations';
import { ShippingInfo } from './ShippingInfo';
import { ApplyCouponForm } from './ApplyCouponForm';
```

You should see two imports that don't exist yet. Let's create them. `ShippingInfo.js` and `ApplyCouponForm.js` are components that will be used in the `CartPage` component to handle two particular actions:

First the `ShippingLocaleForm.js`.

```jsx
import React, { useState } from 'react';
import { useOtherCartMutations } from './useOtherCartMutations';

const ShippingInfo = () => {
  const [country, setCountry] = useState('');
  const [postalCode, setPostalCode] = useState('');
  const { cart } = useSession();
  const {
    updateShippingLocale,
    setShippingMethod,
    savingShippingInfo,
    savingShippingMethod,
  } = useOtherCartMutations();

  const handleSubmit = async (e) => {
    e.preventDefault();
    await updateShippingLocale({ country, postalCode });
  };

  const availableShippingRates = (cart?.availableShippingMethods || [])
    .reduce(
      (rates, nextPackage) => {
        rates.push(...(nextPackage?.rates || []));

        return rates;
      },
      [],
    );

  if (cart.needsShipping && !cart.needsShippingAddress) {
    return (
      <div>
        <h4>Shipping</h4>
        {availableShippingRates.map((shippingRate) => (
          const { cost, id, label } = shippingRate;
          <div key={id}>
            <input
              type="radio"
              name="shipping-methods"
              value={id}
              disabled={savingShippingMethod}
              onChange={(event) => setShippingMethod(event.target.value)}
            />
            <label>
              {`${label}: `}
              <strong>{`$${cost}`}</strong>
            </label>
          </div>
        ))}
        <p>Shipping Tax: {cart.shippingTax}</p>
        <p>Shipping Total: {cart.shippingTotal}</p>
      </div>
    );
  }

  if (cart.needsShipping) {
    return (
      <form onSubmit={handleSubmit}>
        <h4>Shipping Locale</h4>
        <div>
          <label htmlFor="country">Country:</label>
          <input
            type="text"
            id="country"
            name="country"
            value={country}
            onChange={(e) => setCountry(e.target.value)}
          />
        </div>
        <div>
          <label htmlFor="postalCode">Postal Code:</label>
          <input
            type="text"
            id="postalCode"
            name="postalCode"
            value={postalCode}
            onChange={(e) => setPostalCode(e.target.value)}
          />
        </div>
        <button disabled={savingShippingInfo} type="submit">Update Shipping Locale</button>
      </form>
    );
  }

  return null;
};

export default ShippingInfo;
```

This component works by confirming the session shipping requirements and status before returning the proper output. If shipping is needed and a shipping address is set for the customer, the shipping rates are displayed for selection. If shipping is needed and no address is set, then a shipping address form is displayed to set the customer shipping address. If no shipping is needed, `null` is returned.

Simple enough, now the `ApplyCoupon.js`

```jsx
import React, { useState } from 'react';
import { useOtherCartMutations } from './useOtherCartMutations';

const ApplyCoupon = () => {
  const [code, setCode] = useState('');
  const { applyCoupon, removeCoupon } = useOtherCartMutations();

  const handleSubmit = async (e) => {
    e.preventDefault();
    applyCoupon(code);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Apply Coupon:
        <input type="text" value={code} onChange={(e) => setCode(e.target.value)} />
      </label>
      <button type="submit">Apply</button>
    </form>
  );
};

export default ApplyCouponForm;
```

Here we're providing a form to apply a coupon code, which calls the `applyCoupon` function from the `useOtherCartMutations` hook.

Now, onto the `CartPage` component:

```jsx
function CartPage () {
  const { cart } = useSession();
  const { applyCoupon } = useOtherCartMutations();

  if (!cart) {
    return <div>Loading...</div>;
  }

  const cartItems = cart.contents.nodes;

  return (
    <div>
      <h2>Cart</h2>
      <table>
        <thead>
          <tr>
            <th>Product</th>
            <th>Price</th>
            <th>Quantity</th>
            <th>Total</th>
          </tr>
        </thead>
        <tbody>
          {cartItems.map((item) => {
            const { key, product, variation, quantity, subtotal, total } = item;
            const { node: productNode } = product;
            const { node: variationNode } = variation || {};

            const {
              id: productId,
              image,
              name,
            } = productNode;
            const { id: variationId } = variationNode || {};
            const cartMutations = useCartMutations(productId, variationId);

            return (
              <tr key={key}>
                <td>
                  <button onClick={() => cartMutations.removeCartItem()}>Remove</button>
                  <img src={image.sourceUrl} alt={image.altText} />
                  <span>{name}</span>
                </td>
                <td>{subtotal}</td>
                <td>
                  <input
                    type="number"
                    value={quantity}
                    onChange={(e) => cartMutations.updateQuantity(parseInt(e.target.value))}
                  />
                </td>
                <td>{total}</td>
              </tr>
            );
          })}
        </tbody>
        <tfoot>
          <tr>
            <td colSpan={4}>
              <ApplyCoupon />
            </td>
          </tr>
        </tfoot>
      </table>

      <div>
        <h3>Cart Totals</h3>
        <ShippingInfo />
        <p>Subtotal: {cart.subtotal}</p>
        {cart.appliedCoupons.map(({ code, discountAmount }) => (
          <div key={code}>
            <p>
              Coupon: {code} - Discount: {discountAmount}
              <span onClick={() => removeCoupon(code)} style={{ fontWeight: 'bold', color: 'red' }}>
                X
              </span>
            </p>
          </div>
        ))}
        <p>Total: {cart.total}</p>
      </div>
    </div>
  );
};

export default CartPage;
```

In the `CartPage` component, we first fetch the `cart` from the `SessionProvider`. If the cart is not available, we show a loading message. Once the cart is loaded, we display the cart items in a table format, allowing users to remove items or update the quantity.

Lastly, we display the cart's subtotal, applied coupons with their respective discounts and removal buttons, and follow that up with the cart's total.

Now, you can use the `CartPage` component in your app, allowing users to interact with the cart, apply coupons, and manage shipping options.

## Conclusion

With this you're essentially ready to develop a complete application. In the next couple sections we'll be exploring taking the user through checkout.
