Cart
Headless cart component for managing shopping cart functionality with complete UI customization.
Purpose
This library provides a complete cart management system with components for displaying cart items, shipping lines, totals, and cart operations. It's designed as a headless component that provides all the logic while allowing complete UI customization. The Cart component wraps and extends the OrderLines component to provide cart-specific functionality for managing the active order.
Features
- Complete cart state management
- Order line display and manipulation
- Shipping line management
- Cart totals and summary calculations
- Empty cart handling
Installation
- npm
- Yarn
npm install @haus-storefront-react/cart
yarn add @haus-storefront-react/cart
Note: This is not a public package. Contact the Haus Tech Team for access.
API Reference
Cart.Root
The main container component that provides cart context and state management. Must wrap all other Cart components.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | ChildrenProps<CartContextValue> | No | - | Render prop or React node that receives cart context |
Cart.Empty
Displays content when the cart is empty. Automatically hides when the cart has items.
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | React.ReactNode | No | - | Content to display when cart is empty |
asChild | boolean | No | false | Render as child element instead of div |
| ... | WebDivProps | No | - | Additional div props (className, style, etc.) |
Cart.Items
Container for cart items. Renders an OrderLines.Root component internally. Must be used within Cart.Root.
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | React.ReactNode | No | - | Cart item components |
orderCode | string | No | - | Optional order code for fetching specific order |
callbacks | { preAdjust?: (orderLineId: string, quantity: number) => Promise<void>; preRemove?: (lineId: string) => Promise<void> } | No | - | Optional callbacks forwarded to OrderLines.Root |
Cart.Items.Item
Individual cart item component. Wraps OrderLines.Item. Must be used within Cart.Items.Root.
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | React.ReactNode | No | - | Item subcomponents |
orderLine | OrderLine | Yes | - | The order line data to display |
Cart.Items.Image
Displays the product image for the order line. Must be used within Cart.Items.Item.
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
alt | string | No | orderLine.productVariant.name | Alt text for the image |
asChild | boolean | No | false | Render as child element instead of img |
| ... | ImgHTMLAttributes<HTMLImageElement> | No | - | Additional img element props |
Cart.Items.Price
Displays price information for the order line. Must be used within Cart.Items.Item.
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | ChildrenProps<{...}> | No | - | Render prop receiving price information |
withDiscountPrice | boolean | No | false | Whether to show discounted price information |
Cart.Items.Quantity.Root
Container for quantity controls. Must be used within Cart.Items.Item.
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | React.ReactNode | No | - | Quantity control subcomponents |
min | number | No | 0 | Minimum quantity |
max | number | No | - | Maximum quantity |
step | number | No | 1 | Step size for quantity changes |
Subcomponents:
Cart.Items.Quantity.Decrement- Decrease quantity buttonCart.Items.Quantity.Input- Quantity input fieldCart.Items.Quantity.Increment- Increase quantity button
Cart.Items.Remove
Remove item button. Must be used within Cart.Items.Item.
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | React.ReactNode | No | - | Button content |
asChild | boolean | No | false | Render as child element instead of button |
| ... | ButtonHTMLAttributes<HTMLButtonElement> | No | - | Additional button element props |
Cart.ShippingLines
Container for shipping lines. Automatically hides when there are no shipping lines. Must be used within Cart.Root.
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | React.ReactNode | No | - | Shipping line components |
asChild | boolean | No | false | Render as child element instead of div |
| ... | WebDivProps | No | - | Additional div props |
Cart.ShippingLine
Individual shipping line component. Must be used within Cart.ShippingLines.
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
shippingLine | ShippingLineType | Yes | - | The shipping line data to display |
children | React.ReactNode | No | - | Shipping line subcomponents |
asChild | boolean | No | false | Render as child element instead of div |
| ... | WebDivProps | No | - | Additional div props |
Cart.ShippingLine.Name
Displays the shipping method name. Must be used within Cart.ShippingLine.
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
shippingLineId | string | Yes | - | ID of the shipping line |
asChild | boolean | No | false | Render as child element instead of span |
| ... | WebSpanProps | No | - | Additional span props |
Note: This component automatically finds the shipping line by ID from the cart context.
Cart.ShippingLine.Price
Displays price information for the shipping line. Must be used within Cart.ShippingLine.
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | ChildrenProps<{...}> | No | - | Render prop receiving price information |
shippingLineId | string | Yes | - | ID of the shipping line |
Cart.Summary
Cart totals and checkout summary. Automatically hides when cart is empty. Must be used within Cart.Root.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | ChildrenProps<{...}> | No | - | Render prop receiving summary data |
asChild | boolean | No | false | Render as child element instead of div |
| ... | WebDivProps (except children) | No | - | Additional div props |
Returns
| Property | Type | Description |
|---|---|---|
totalPrice | number | Order total price (without tax) |
totalWithTax | number | Order total price (with tax) |
subTotal | number | Order subtotal (without tax) |
subTotalWithTax | number | Order subtotal (with tax) |
shipping | number | Shipping cost (without tax) |
shippingWithTax | number | Shipping cost (with tax) |
taxSummary | OrderTaxSummary[] | Array of tax summary entries |
currencyCode | CurrencyCode | Currency code for the order |
useCartProps
Hook that provides cart context value. Used internally by Cart.Root.
Returns
| Return Value | Type | Description |
|---|---|---|
data | Order | null | The current order/cart data or null |
isLoading | boolean | Loading state flag |
error | Error | null | Error object if cart loading failed |
totalItems | number | Total number of items in cart |
totalPrice | number | Total price of cart items |
currencyCode | CurrencyCode | Currency code for the cart |
Basic Usage
Simple Cart Display
Your cart is empty
Add some items to get started
- React
- React Native
import { Cart } from '@haus-storefront-react/cart'
import { formatPrice } from '@haus-storefront-react/common-utils'
function ShoppingCart() {
return (
<Cart.Root>
{({ isLoading, error, totalItems }) => (
<div>
{isLoading && <div>Loading cart...</div>}
{error && <div>Error: {error.message}</div>}
{totalItems > 0 ? (
<>
<Cart.Items.Root>
{({ orderLines }) =>
orderLines?.map((orderLine) => (
<Cart.Items.Item key={orderLine.id} orderLine={orderLine}>
<Cart.Items.Image />
<Cart.Items.Price />
<Cart.Items.Quantity.Root>
<Cart.Items.Quantity.Decrement />
<Cart.Items.Quantity.Input />
<Cart.Items.Quantity.Increment />
</Cart.Items.Quantity.Root>
<Cart.Items.Remove>Remove</Cart.Items.Remove>
</Cart.Items.Item>
))
}
</Cart.Items.Root>
<Cart.Summary>
{({ subTotal, subTotalWithTax, currencyCode }) => (
<div className='cart-summary'>
<div>Subtotal: {formatPrice(subTotal, currencyCode)}</div>
<div>Total: {formatPrice(subTotalWithTax, currencyCode)}</div>
<button>Checkout</button>
</div>
)}
</Cart.Summary>
</>
) : (
<Cart.Empty>
<h2>Your cart is empty</h2>
<p>Add some items to get started</p>
<button>Browse Products</button>
</Cart.Empty>
)}
</div>
)}
</Cart.Root>
)
}
import { Text, Pressable } from 'react-native'
import { Cart } from '@haus-storefront-react/cart'
import { formatPrice } from '@haus-storefront-react/common-utils'
function ShoppingCart() {
return (
<Cart.Root>
{({ isLoading, error, totalItems }) => (
<>
{isLoading && <Text>Loading cart...</Text>}
{error && <Text>Error: {error.message}</Text>}
{totalItems > 0 ? (
<>
<Cart.Items.Root>
{({ orderLines }) =>
orderLines?.map((orderLine) => (
<Cart.Items.Item key={orderLine.id} orderLine={orderLine}>
<Cart.Items.Image />
<Cart.Items.Price />
<Cart.Items.Quantity.Root>
<Cart.Items.Quantity.Decrement />
<Cart.Items.Quantity.Input />
<Cart.Items.Quantity.Increment />
</Cart.Items.Quantity.Root>
<Cart.Items.Remove>Remove</Cart.Items.Remove>
</Cart.Items.Item>
))
}
</Cart.Items.Root>
<Cart.Summary>
{({ subTotal, subTotalWithTax, currencyCode }) => (
<>
<Text>Subtotal: {formatPrice(subTotal, currencyCode)}</Text>
<Text>Total: {formatPrice(subTotalWithTax, currencyCode)}</Text>
<Pressable>
<Text>Checkout</Text>
</Pressable>
</>
)}
</Cart.Summary>
</>
) : (
<Cart.Empty>
<Text>Your cart is empty</Text>
<Text>Add some items to get started</Text>
<Pressable>
<Text>Browse Products</Text>
</Pressable>
</Cart.Empty>
)}
</>
)}
</Cart.Root>
)
}
Advanced Usage
Complex Cart with Custom Styling
- React
- React Native
import { Cart } from '@haus-storefront-react/cart'
import { QuantityButtons, Price } from '@haus-storefront-react/common-ui'
function CustomCart() {
return (
<Cart.Root>
{({ data: order, totalItems }) => (
<div className='cart-container'>
<h1>Shopping Cart ({totalItems} items)</h1>
{totalItems > 0 ? (
<div className='cart-content'>
<Cart.Items.Root>
{({ orderLines }) =>
orderLines?.map((orderLine) => (
<div key={orderLine.id} className='cart-item'>
<Cart.Items.Item orderLine={orderLine}>
<Cart.Items.Image className='item-image' />
<div className='item-details'>
<h3>{orderLine.productVariant.product.name}</h3>
<p>{orderLine.productVariant.name}</p>
<Cart.Items.Price asChild>
<Price.Root
price={orderLine.unitPrice}
priceWithTax={orderLine.unitPriceWithTax}
currencyCode={order.currencyCode}
className='item-price'
>
<Price.Amount />
</Price.Root>
</Cart.Items.Price>
</div>
<div className='item-controls'>
<Cart.Items.Quantity.Root asChild>
<QuantityButtons.Root
value={orderLine.quantity}
onValueChange={(qty) =>
adjustQuantity(orderLine.id, qty)
}
min={1}
max={99}
>
<QuantityButtons.Decrement className='qty-btn' />
<QuantityButtons.Input className='qty-value' />
<QuantityButtons.Increment className='qty-btn' />
</QuantityButtons.Root>
</Cart.Items.Quantity.Root>
<Cart.Items.Remove asChild>
<button className='remove-btn'>Remove</button>
</Cart.Items.Remove>
</div>
</Cart.Items.Item>
</div>
))
}
</Cart.Items.Root>
<Cart.ShippingLines className='shipping-section'>
<h3>Shipping</h3>
{order?.shippingLines.map((shippingLine) => (
<Cart.ShippingLine
key={shippingLine.id}
shippingLine={shippingLine}
>
<div className='shipping-line'>
<Cart.ShippingLine.Name
shippingLineId={shippingLine.id}
className='shipping-name'
/>
<Cart.ShippingLine.Price
shippingLineId={shippingLine.id}
asChild
>
<Price.Root
price={shippingLine.price}
priceWithTax={shippingLine.priceWithTax}
currencyCode={order.currencyCode}
className='shipping-price'
>
<Price.Amount />
</Price.Root>
</Cart.ShippingLine.Price>
</div>
</Cart.ShippingLine>
))}
</Cart.ShippingLines>
<Cart.Summary className='cart-summary'>
{({
totalPrice,
totalWithTax,
subTotal,
subTotalWithTax,
shipping,
shippingWithTax,
taxSummary,
currencyCode,
}) => (
<div className='summary-details'>
<div className='summary-line'>
<span>Subtotal:</span>
<Price.Root
price={subTotal}
priceWithTax={subTotalWithTax}
currencyCode={currencyCode}
>
<Price.Amount />
</Price.Root>
</div>
{shipping > 0 && (
<div className='summary-line'>
<span>Shipping:</span>
<Price.Root
price={shipping}
priceWithTax={shippingWithTax}
currencyCode={currencyCode}
>
<Price.Amount />
</Price.Root>
</div>
)}
{taxSummary.length > 0 && (
<div className='tax-summary'>
{taxSummary.map((tax, index) => (
<div key={index} className='summary-line'>
<span>{tax.description}:</span>
<Price.Root
price={tax.taxTotal}
priceWithTax={tax.taxTotal}
currencyCode={currencyCode}
>
<Price.Amount />
</Price.Root>
</div>
))}
</div>
)}
<div className='summary-line total'>
<span>Total:</span>
<Price.Root
price={totalPrice}
priceWithTax={totalWithTax}
currencyCode={currencyCode}
>
<Price.Amount />
</Price.Root>
</div>
<button className='checkout-btn'>
Proceed to Checkout
</button>
</div>
)}
</Cart.Summary>
</div>
) : (
<Cart.Empty className='empty-cart'>
<div className='empty-content'>
<h2>Your cart is empty</h2>
<p>Looks like you haven't added any items to your cart yet.</p>
<button className='browse-btn'>Browse Products</button>
</div>
</Cart.Empty>
)}
</div>
)}
</Cart.Root>
)
}
import { View, Text, Pressable } from 'react-native'
import { Cart } from '@haus-storefront-react/cart'
import { QuantityButtons, Price } from '@haus-storefront-react/common-ui'
function CustomCart() {
return (
<Cart.Root>
{({ data: order, totalItems }) => (
<View>
<Text>Shopping Cart ({totalItems} items)</Text>
{totalItems > 0 ? (
<View>
<Cart.Items.Root>
{({ orderLines }) =>
orderLines?.map((orderLine) => (
<Cart.Items.Item key={orderLine.id} orderLine={orderLine}>
<Cart.Items.Image />
<View>
<Text>{orderLine.productVariant.product.name}</Text>
<Text>{orderLine.productVariant.name}</Text>
<Cart.Items.Price />
</View>
<Cart.Items.Quantity.Root asChild>
<QuantityButtons.Root
value={orderLine.quantity}
onValueChange={(qty) =>
adjustQuantity(orderLine.id, qty)
}
min={1}
max={99}
>
<QuantityButtons.Decrement />
<QuantityButtons.Input />
<QuantityButtons.Increment />
</QuantityButtons.Root>
</Cart.Items.Quantity.Root>
<Cart.Items.Remove>Remove</Cart.Items.Remove>
</Cart.Items.Item>
))
}
</Cart.Items.Root>
<Cart.Summary>
{({
totalPrice,
totalWithTax,
currencyCode,
}) => (
<View>
<Text>Total: {totalWithTax}</Text>
<Pressable>
<Text>Proceed to Checkout</Text>
</Pressable>
</View>
)}
</Cart.Summary>
</View>
) : (
<Cart.Empty>
<Text>Your cart is empty</Text>
<Text>Looks like you haven't added any items yet.</Text>
<Pressable>
<Text>Browse Products</Text>
</Pressable>
</Cart.Empty>
)}
</View>
)}
</Cart.Root>
)
}
Conditional Rendering Patterns
- React
- React Native
import { Cart } from '@haus-storefront-react/cart'
import { formatPrice } from '@haus-storefront-react/common-utils'
function ConditionalCart() {
return (
<Cart.Root>
{({ data: order, isLoading, totalItems }) => {
if (isLoading) {
return <div className='loading'>Loading cart...</div>
}
if (!order || totalItems === 0) {
return (
<Cart.Empty>
<h2>Your cart is empty</h2>
<button>Start Shopping</button>
</Cart.Empty>
)
}
return (
<div className='cart-full'>
<Cart.Items.Root>
{({ orderLines }) =>
orderLines?.map((orderLine) => (
<Cart.Items.Item key={orderLine.id} orderLine={orderLine}>
<Cart.Items.Image />
<div>
<h3>{orderLine.productVariant.product.name}</h3>
<Cart.Items.Price />
</div>
<Cart.Items.Quantity.Root>
<Cart.Items.Quantity.Decrement />
<Cart.Items.Quantity.Input />
<Cart.Items.Quantity.Increment />
</Cart.Items.Quantity.Root>
<Cart.Items.Remove>Remove</Cart.Items.Remove>
</Cart.Items.Item>
))
}
</Cart.Items.Root>
<Cart.Summary>
{({ totalWithTax, currencyCode }) => (
<div className='checkout-section'>
<div>Total: {formatPrice(totalWithTax, currencyCode)}</div>
<button>Checkout</button>
</div>
)}
</Cart.Summary>
</div>
)
}}
</Cart.Root>
)
}
import { View, Text, Pressable } from 'react-native'
import { Cart } from '@haus-storefront-react/cart'
import { formatPrice } from '@haus-storefront-react/common-utils'
function ConditionalCart() {
return (
<Cart.Root>
{({ data: order, isLoading, totalItems }) => {
if (isLoading) {
return <Text>Loading cart...</Text>
}
if (!order || totalItems === 0) {
return (
<Cart.Empty>
<Text>Your cart is empty</Text>
<Pressable>
<Text>Start Shopping</Text>
</Pressable>
</Cart.Empty>
)
}
return (
<View>
<Cart.Items.Root>
{({ orderLines }) =>
orderLines?.map((orderLine) => (
<Cart.Items.Item key={orderLine.id} orderLine={orderLine}>
<Cart.Items.Image />
<View>
<Text>{orderLine.productVariant.product.name}</Text>
<Cart.Items.Price />
</View>
<Cart.Items.Quantity.Root>
<Cart.Items.Quantity.Decrement />
<Cart.Items.Quantity.Input />
<Cart.Items.Quantity.Increment />
</Cart.Items.Quantity.Root>
<Cart.Items.Remove>Remove</Cart.Items.Remove>
</Cart.Items.Item>
))
}
</Cart.Items.Root>
<Cart.Summary>
{({ totalWithTax, currencyCode }) => (
<View>
<Text>Total: {formatPrice(totalWithTax, currencyCode)}</Text>
<Pressable>
<Text>Checkout</Text>
</Pressable>
</View>
)}
</Cart.Summary>
</View>
)
}}
</Cart.Root>
)
}
Made with ❤️ by Haus Tech Team