Skip to main content

CartItems

Description

A headless render props component that provides cart items data and operations. It reads from CartContext and exposes items, loading states, item count, and a remove handler — but renders no UI of its own.

Role in Theming

CartItems is one of EverShop's headless components — it owns the data access and operations while leaving all UI decisions to its parent:

  • CartItems renders nothing. It returns only what its children function renders.
  • Theme developers do not override CartItems. Instead, they override the parent components that consume it (DefaultCartItemList, DefaultMiniCartDropdown).
  • The data layer stays stable across themes. Item data extraction, tax display logic, loading states, and remove operations are encapsulated in CartItems.

Theme Override Points

CartItems is consumed by these components, which are the actual override targets for theme developers:

Parent ComponentRouteOverride Path in Theme
DefaultCartItemListcartthemes/<name>/src/pages/cart/DefaultCartItemList.tsx
DefaultMiniCartDropdownall (via MiniCart)themes/<name>/src/pages/all/DefaultMiniCartDropdown.tsx

Import

import { CartItems } from '@components/frontStore/cart/CartItems';

Usage

import { CartItems } from '@components/frontStore/cart/CartItems';

function Cart() {
return (
<CartItems>
{({ items, loading, isEmpty, totalItems, onRemoveItem }) => (
<div>
{isEmpty ? (
<p>Your cart is empty</p>
) : (
<ul>
{items.map(item => (
<li key={item.cartItemId}>
{item.productName} - {item.qty}
<button onClick={() => onRemoveItem(item.cartItemId)}>
Remove
</button>
</li>
))}
</ul>
)}
</div>
)}
</CartItems>
);
}

Props

NameTypeRequiredDescription
childrenRenderFunctionYesRender function receiving cart data

Render Function Props

The render function receives an object with:

NameTypeDescription
itemsCartItem[]Array of cart items
showPriceIncludingTaxbooleanWhether to show prices with tax
loadingbooleanTrue when cart is loading
isEmptybooleanTrue when cart has no items
totalItemsnumberTotal quantity of items
onRemoveItem(itemId: string) => Promise<void>Function to remove an item

CartItem Interface

Each item in the items array has:

interface CartItem {
cartItemId: string;
productId: string;
productSku: string;
productName: string;
productUrl: string;
thumbnail?: string;
qty: number;
productPrice: { value: number; text: string };
finalPrice: { value: number; text: string };
finalPriceInclTax: { value: number; text: string };
lineTotal: { value: number; text: string };
lineTotalInclTax: { value: number; text: string };
subTotal: { value: number; text: string };
variantOptions?: Array<{
attributeCode: string;
attributeName: string;
optionText: string;
}>;
}

Price Fields

Different price fields available:

  • productPrice: Base product price
  • productPriceInclTax: Base price with tax
  • finalPrice: Price after discounts (excl. tax)
  • finalPriceInclTax: Price after discounts (incl. tax)
  • lineTotal: Item total (qty x price, excl. tax)
  • lineTotalInclTax: Item total (qty x price, incl. tax)
  • subTotal: Subtotal for the item

Use showPriceIncludingTax to determine which price to display.

Examples

Basic Cart List

import { CartItems } from '@components/frontStore/cart/CartItems';

function ShoppingCart() {
return (
<CartItems>
{({ items, loading, isEmpty, onRemoveItem }) => {
if (loading) {
return <div>Loading cart...</div>;
}

if (isEmpty) {
return <p>Your cart is empty</p>;
}

return (
<div>
{items.map(item => (
<div key={item.cartItemId}>
<h3>{item.productName}</h3>
<p>Price: {item.productPrice.text}</p>
<p>Quantity: {item.qty}</p>
<button onClick={() => onRemoveItem(item.cartItemId)}>
Remove
</button>
</div>
))}
</div>
);
}}
</CartItems>
);
}

With Tax-Aware Pricing

import { CartItems } from '@components/frontStore/cart/CartItems';

function CartTable() {
return (
<CartItems>
{({ items, showPriceIncludingTax, onRemoveItem }) => (
<table>
<thead>
<tr>
<th>Product</th>
<th>Price</th>
<th>Quantity</th>
<th>Total</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{items.map(item => {
const price = showPriceIncludingTax
? item.finalPriceInclTax
: item.finalPrice;
const total = showPriceIncludingTax
? item.lineTotalInclTax
: item.lineTotal;

return (
<tr key={item.cartItemId}>
<td>{item.productName}</td>
<td>{price.text}</td>
<td>{item.qty}</td>
<td>{total.text}</td>
<td>
<button onClick={() => onRemoveItem(item.cartItemId)}>
Remove
</button>
</td>
</tr>
);
})}
</tbody>
</table>
)}
</CartItems>
);
}

Theme Override Example

A theme developer overrides DefaultCartItemList to provide a custom cart item layout. Create this file in your theme:

themes/my-theme/src/pages/cart/DefaultCartItemList.tsx

import { CartItems } from '@components/frontStore/cart/CartItems';
import { ItemQuantity } from '@components/frontStore/cart/ItemQuantity';

function DefaultCartItemList() {
return (
<CartItems>
{({ items, loading, isEmpty, showPriceIncludingTax, onRemoveItem }) => {
if (isEmpty) {
return (
<div className="my-theme-empty-cart">
<p>Nothing here yet</p>
<a href="/products">Start Shopping</a>
</div>
);
}

return (
<div className="my-theme-cart-items">
{items.map(item => {
const price = showPriceIncludingTax
? item.finalPriceInclTax
: item.finalPrice;

return (
<div key={item.cartItemId} className="my-theme-cart-item">
{item.thumbnail && (
<img src={item.thumbnail} alt={item.productName} />
)}
<div>
<h3>{item.productName}</h3>
<p>{price.text}</p>
{item.variantOptions?.map(opt => (
<span key={opt.attributeCode}>
{opt.attributeName}: {opt.optionText}
</span>
))}
</div>
<ItemQuantity
cartItemId={item.cartItemId}
initialValue={item.qty}
>
{({ quantity, increase, decrease, loading: qtyLoading }) => (
<div>
<button onClick={decrease} disabled={qtyLoading}>-</button>
<span>{quantity}</span>
<button onClick={increase} disabled={qtyLoading}>+</button>
</div>
)}
</ItemQuantity>
<button onClick={() => onRemoveItem(item.cartItemId)}>
Remove
</button>
</div>
);
})}
</div>
);
}}
</CartItems>
);
}

export default DefaultCartItemList;

export const layout = {
areaId: 'shoppingCartLeft',
sortOrder: 10
};

Features

  • Headless: Renders no UI — full control via render props
  • Loading State: Automatic loading management
  • Empty State: isEmpty flag for easy empty cart handling
  • Item Count: Total items count
  • Remove Handler: Built-in remove functionality
  • Tax Configuration: Respects tax display settings
  • Type Safe: Full TypeScript support


Support us


EverShop is an open-source project that relies on community support. If you find our project useful, please consider sponsoring us.