Skip to content

Confirm an add to cart

Every way into the cart goes through one hook, useAddToCart: the add to cart button, a reorder, a quote ordered into the cart, a shopping list, and your own forms. So the confirmation is set once, for the whole site, and every add shows it.

What you want Use
A sheet with the product, its price and suggestions to go with it The shipped sheet
A toast, a dialog or a mini-cart of your own Your own confirmation
Something else on one page, such as a cross-sell dialog One page differently
Nothing: the cart badge in the header is enough Nothing to do. CartBadge already announces the new count.

CartAddedSheet from @evolve-storefront/feature-cart is the confirmation the package ships. Mount it once, inside your CartProvider:

import { CartAddedSheet } from "@evolve-storefront/feature-cart/cart-added-sheet";
import { CartProvider } from "@evolve-storefront/feature-cart/provider";
import Link from "next/link";
<CartProvider>
{children}
<CartAddedSheet
renderCartLink={(children) => <Link href="/cart">{children}</Link>}
renderLink={(line, children) => <Link href={`/products/${line.productSlug}`}>{children}</Link>}
renderCrossSell={(line) => <RelatedProducts slug={line.productSlug} limit={2} />}
/>
</CartProvider>

renderCartLink is the only prop it needs. The sheet then does this on every add:

  • One product shows its image, name, the quantity added and its price.
  • Several lines at once, such as a reorder, show how many lines and items went in and what they cost, as gross or net prices, following the provider’s vatDisplay.
  • Lines the cart refused are listed with the reason: out of stock, below the minimum quantity, no price, or an unknown product.
  • Suggestions come from renderCrossSell, so the product query stays yours. The sheet asks for them only when a consumer adds one product, never for a bulk add or a business buyer.

It does not close on its own. Escape, the backdrop, “Keep shopping” and “Go to cart” close it, and focus goes back to the button that added. If you build your own add button, keep it focusable while it adds: use aria-disabled, not disabled, or focus has nowhere to go back to.

The sheet is a Sheet from @evolve-storefront/ui, so it takes side and the other sheet props, and follows your brand tokens.

Leave CartAddedSheet out, and pass onLineItemsAdded to the CartProvider instead. Use one or the other: with both, the shopper gets two confirmations.

  1. Mount the toaster once, in your root layout:

    import { Toaster } from "@evolve-storefront/ui/toaster";
    <Toaster position="bottom-right" />
  2. Show a toast from the provider:

    import { CartProvider } from "@evolve-storefront/feature-cart/provider";
    import { toast } from "sonner";
    <CartProvider
    onLineItemsAdded={({ addedItems, requested, warnings }) => {
    const name = addedItems[0]?.productName ?? "";
    if (warnings.length > 0) {
    toast.warning(`${name} added, but stock limited the quantity`);
    return;
    }
    toast.success(`${requested[0]?.quantity} × ${name} added to your cart`);
    }}
    >
    {children}
    </CartProvider>

For a dialog or a mini-cart, keep the result in state and open your own Dialog or Sheet from @evolve-storefront/ui with it. The reference sites do this. They keep the added lines in a React context of their own, and their added-to-cart dialog opens whenever it holds any.

The callback receives one object:

Field What it holds
addedItems The cart’s lines for the products just added, after the write. A product that was already in the cart gives its line with the new total quantity.
requested What was asked for. Its quantity is the amount just added: use it to say “2 added”.
cart The cart the write returned.
warnings Problems that did not stop the add. OUT_OF_STOCK means the add worked, but stock capped the quantity.
failed The lines the cart refused, each with its reason. They are not in addedItems.

A page can replace the confirmation for its own adds. Pass onAdded to the button, or to the hook when you add from your own form:

import { CartAddProductButton } from "@evolve-storefront/feature-cart/cart-add-product-button";
<CartAddProductButton
sku={variant.sku}
quantity={quantity}
onAdded={(result) => openCrossSellDialog(result.addedItems[0])}
/>
const add = useAddToCart({ onAdded: (result) => openCrossSellDialog(result.addedItems[0]) });

onAdded replaces the site’s confirmation, it does not add to it. Adds with an onAdded open neither the sheet nor your onLineItemsAdded, so a shopper never sees a cross-sell dialog and a mini-cart at the same time.

The hook announces every add to screen readers, whichever confirmation you choose, and also when you show none. If a page announces the result itself, pass useAddToCart({ silent: true }) so it is not announced twice.

  • Change how the sheet, or any other component, looks: Customize a component.
  • Add more than a quantity, such as a color picked in your own form: see “Add to cart with your own fields” in node_modules/@evolve-storefront/feature-cart/CUSTOMIZING.md.