Add Item to Order
Headless component for managing adding items to an order with quantity selection and add-to-cart functionality.
Purpose
This library provides a comprehensive system for adding products to the shopping cart, including quantity selection and add-to-cart functionality. It's designed as a headless component that provides all the logic while allowing complete UI customization. The component manages state for whether an item is in the cart, handles loading states, and provides callbacks for custom validation logic before adding or adjusting items.
Features
- Add items to order with quantity selection
- Adjust quantity of existing order lines
- Customizable callbacks for pre-add and pre-adjust validation
- Support for render props and regular children patterns
- Optimistic updates for better UX
- Automatic cart state management
- Cross-platform support (web and native via asChild pattern)
- Quantity controls with min/max/step constraints
Installation
- npm
- Yarn
npm install @haus-storefront-react/add-item-to-order
yarn add @haus-storefront-react/add-item-to-order
Note: This is not a public package. Contact the Haus Tech Team for access.
API Reference
AddItemToOrder.Root
Main container component that provides add-to-cart context and state. Can be used with either regular children or a render prop function. Must wrap all other AddItemToOrder components.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
productVariantId | string | Yes | - | The ID of the product variant to add to the order |
callbacks | Callbacks | No | undefined | Optional callbacks for custom logic before operations |
modifyData | (data: Order) => Order | No | undefined | Optional function to modify the order before cart checks |
children | ChildrenProps<AddItemToOrderContextValue> | No | - | Either React nodes or a function that receives the context value |
asChild | boolean | No | false | When true, merges props with the child component |
Context Value (when using render prop)
The render prop function receives an object with the following properties:
| Property | Type | Description |
|---|---|---|
productVariantId | string | The ID of the product variant |
isInCart | boolean | undefined | Whether the item is in the cart (undefined while loading) |
isLoading | boolean | Whether an add or adjust-quantity operation is in progress |
error | Error | null | Error from add-to-cart or adjust quantity (e.g. INSUFFICIENT_STOCK_ERROR) |
addItemToOrder | (quantity: number) => Promise<Maybe<Order>> | Function to add item to order |
orderLineId | string | undefined | The ID of the order line if item is in cart |
adjustOrderLine | (orderLineId: string, quantity: number) => Promise<Order> | Function to adjust quantity for an existing order line |
orderLines | OrderLine[] | undefined | Current order lines from the active order |
callbacks | Callbacks | undefined | The callbacks passed to the Root component |
modifyData | ((data: Order) => Order) | undefined | The modifyData function passed to the Root component |
Callbacks Object
interface Callbacks {
preAdd?: (productVariantId: string, quantity: number) => Promise<void>
preAdjust?: (orderLineId: string, quantity: number) => Promise<void>
}
AddItemToOrder.Button
Button component for adding an item to the order. Must be used within AddItemToOrder.Root. Supports the asChild pattern for cross-platform compatibility.
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
asChild | boolean | No | false | When true, merges props with the child component |
children | ReactNode | No | - | Button content |
...props | WebButtonProps | No | - | Additional button props (onClick, disabled, etc.) |
AddItemToOrder.Quantity.Root
Quantity controls container component. Must be used within AddItemToOrder.Root. Renders quantity controls for an existing order line.
Sub-components:
AddItemToOrder.Quantity.Decrement- Button to decrease quantity (accepts standard button props andasChild)AddItemToOrder.Quantity.Input- Input field for displaying and editing quantity (accepts standard input props)AddItemToOrder.Quantity.Increment- Button to increase quantity (accepts standard button props andasChild)
Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
min | number | No | 0 | Minimum quantity |
max | number | No | undefined | Maximum quantity |
step | number | No | 1 | Step size for quantity changes |
asChild | boolean | No | false | When true, merges props with the child component |
children | ReactNode | No | - | Quantity control children (Decrement, Input, Increment) |
useAddItemToOrderProps
Hook that powers the AddItemToOrder compound components. It exposes add-to-cart state, mutation helpers, and prebuilt button props for custom implementations.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
productVariantId | string | Yes | Product variant ID to manage in cart |
callbacks | { preAdd?: ...; preAdjust?: ...} | No | Optional async callbacks executed before add/adjust operations |
modifyData | (data: Order) => Order | No | Optional function to normalize order data before in-cart evaluation |
Returns
| Return Value | Type | Description |
|---|---|---|
addItemToOrder | (quantity: number) => Promise<Maybe<Order>> | Function to add the variant with specified quantity |
error | Error | null | Mutation error state |
isLoading | boolean | Loading state for add operation |
getButtonProps | () => { onClick: () => void; disabled: boolean; ... } | Helper for wiring an "Add to cart" button |
isInCart | boolean | undefined | Whether the product variant currently exists in cart |
Basic Usage
Simple Component
- React
- React Native
import { AddItemToOrder } from '@haus-storefront-react/add-item-to-order'
function MyComponent({ product }) {
const defaultVariant = product.variants[0]
return (
<div>
<AddItemToOrder.Root productVariantId={defaultVariant.id}>
{({ isInCart, isLoading, error }) => (
<>
{!isInCart && (
<AddItemToOrder.Button>
{isLoading ? 'Adding...' : 'Add to Cart'}
</AddItemToOrder.Button>
)}
{isInCart && (
<AddItemToOrder.Quantity.Root>
<AddItemToOrder.Quantity.Decrement>
-
</AddItemToOrder.Quantity.Decrement>
<AddItemToOrder.Quantity.Input />
<AddItemToOrder.Quantity.Increment>
+
</AddItemToOrder.Quantity.Increment>
</AddItemToOrder.Quantity.Root>
)}
{error && <div>Error: {error.message}</div>}
</>
)}
</AddItemToOrder.Root>
</div>
)
}
import { Text } from 'react-native'
import { AddItemToOrder } from '@haus-storefront-react/add-item-to-order'
function MyComponent({ product }) {
const defaultVariant = product.variants[0]
return (
<AddItemToOrder.Root productVariantId={defaultVariant.id}>
{({ isInCart, isLoading, error }) => (
<>
{!isInCart && (
<AddItemToOrder.Button>
{isLoading ? 'Adding...' : 'Add to Cart'}
</AddItemToOrder.Button>
)}
{isInCart && (
<AddItemToOrder.Quantity.Root>
<AddItemToOrder.Quantity.Decrement>-</AddItemToOrder.Quantity.Decrement>
<AddItemToOrder.Quantity.Input />
<AddItemToOrder.Quantity.Increment>+</AddItemToOrder.Quantity.Increment>
</AddItemToOrder.Quantity.Root>
)}
{error && <Text>Error: {error.message}</Text>}
</>
)}
</AddItemToOrder.Root>
)
}
Basic Hook Usage
- React
- React Native
import { AddItemToOrder } from '@haus-storefront-react/add-item-to-order'
function MyComponent({ productVariantId }) {
return (
<AddItemToOrder.Root productVariantId={productVariantId}>
{({ isInCart, isLoading, error, addItemToOrder }) => (
<div>
{!isInCart && (
<button onClick={() => addItemToOrder(1)} disabled={isLoading}>
{isLoading ? 'Adding...' : 'Add to Cart'}
</button>
)}
{isInCart && <div>Item is in cart</div>}
{error && <div>Error: {error.message}</div>}
</div>
)}
</AddItemToOrder.Root>
)
}
import { Pressable, Text } from 'react-native'
import { AddItemToOrder } from '@haus-storefront-react/add-item-to-order'
function MyComponent({ productVariantId }) {
return (
<AddItemToOrder.Root productVariantId={productVariantId}>
{({ isInCart, isLoading, addItemToOrder }) => (
<>
{!isInCart && (
<Pressable
onPress={() => addItemToOrder(1)}
disabled={isLoading}
>
<Text>{isLoading ? 'Adding...' : 'Add to Cart'}</Text>
</Pressable>
)}
{isInCart && <Text>Item is in cart</Text>}
</>
)}
</AddItemToOrder.Root>
)
}
Advanced Usage
Custom Callbacks
- React
- React Native
<AddItemToOrder.Root
productVariantId='variant-123'
callbacks={{
preAdd: async (productVariantId, quantity) => {
// Custom validation logic before adding
if (quantity > 10) {
throw new Error('Maximum quantity is 10')
}
// Optional: Show confirmation dialog
const confirmed = await confirm('Add to cart?')
if (!confirmed) {
throw new Error('Cancelled by user')
}
},
preAdjust: async (orderLineId, quantity) => {
// Custom logic before adjusting quantity
console.log(
'Adjusting order line:',
orderLineId,
'to quantity:',
quantity,
)
},
}}
>
{/* Component content */}
</AddItemToOrder.Root>
import { Alert } from 'react-native'
<AddItemToOrder.Root
productVariantId='variant-123'
callbacks={{
preAdd: async (productVariantId, quantity) => {
// Custom validation logic before adding
if (quantity > 10) {
throw new Error('Maximum quantity is 10')
}
// Optional: Show confirmation dialog
const confirmed = await new Promise<boolean>((resolve) => {
Alert.alert('Add to cart?', '', [
{ text: 'Cancel', style: 'cancel', onPress: () => resolve(false) },
{ text: 'Confirm', onPress: () => resolve(true) },
])
})
if (!confirmed) {
throw new Error('Cancelled by user')
}
},
preAdjust: async (orderLineId, quantity) => {
// Custom logic before adjusting quantity
console.log('Adjusting order line:', orderLineId, 'to quantity:', quantity)
},
}}
>
{/* Component content */}
</AddItemToOrder.Root>
Modifying Order Data with modifyData
Use modifyData to transform the order before the component derives its state. Useful for filtering duplicate productVariant IDs or excluding certain lines.
- React
- React Native
<AddItemToOrder.Root
productVariantId={variant.id}
modifyData={(order) => ({
...order,
// Example: exclude gift lines from cart state
lines: order.lines.filter((line) => !line.customFields?.gift),
})}
>
{/* Component content */}
</AddItemToOrder.Root>
<AddItemToOrder.Root
productVariantId={variant.id}
modifyData={(order) => ({
...order,
// Example: exclude gift lines from cart state
lines: order.lines.filter((line) => !line.customFields?.gift),
})}
>
{/* Component content */}
</AddItemToOrder.Root>
Component Configuration
- React
- React Native
import { AddItemToOrder } from '@haus-storefront-react/add-item-to-order'
function AdvancedComponent({ productVariantId }) {
return (
<AddItemToOrder.Root
productVariantId={productVariantId}
callbacks={{
preAdd: async (productVariantId, quantity) => {
if (quantity > 10) {
throw new Error('Maximum quantity is 10')
}
},
preAdjust: async (orderLineId, quantity) => {
console.log('Adjusting order line:',
orderLineId,
'to quantity:',
quantity,
)
},
}}
modifyData={(order) => ({
...order,
lines: order.lines.filter((line) => !line.customFields?.gift),
})}
>
{({ isInCart, isLoading, error }) => (
<div className='advanced-add-to-cart'>
{!isInCart ? (
<AddItemToOrder.Button>
{isLoading ? 'Adding...' : 'Add to Cart'}
</AddItemToOrder.Button>
) : (
<AddItemToOrder.Quantity.Root min={0} max={10} step={1}>
<AddItemToOrder.Quantity.Decrement>
-
</AddItemToOrder.Quantity.Decrement>
<AddItemToOrder.Quantity.Input />
<AddItemToOrder.Quantity.Increment>
+
</AddItemToOrder.Quantity.Increment>
</AddItemToOrder.Quantity.Root>
)}
{error && <div className='error-message'>{error.message}</div>}
</div>
)}
</AddItemToOrder.Root>
)
}
import { View, Text, Pressable } from 'react-native'
import { AddItemToOrder } from '@haus-storefront-react/add-item-to-order'
function AdvancedComponent({ productVariantId }) {
return (
<AddItemToOrder.Root
productVariantId={productVariantId}
callbacks={{
preAdd: async (_id, quantity) => {
if (quantity > 10) {
throw new Error('Maximum quantity is 10')
}
},
preAdjust: async (orderLineId, quantity) => {
console.log('Adjusting order line:', orderLineId, 'to quantity:', quantity)
},
}}
modifyData={(order) => ({
...order,
lines: order.lines.filter((line) => !line.customFields?.gift),
})}
>
{({ isInCart, isLoading, error }) => (
<View>
{!isInCart ? (
<AddItemToOrder.Button>
<Text>
{isLoading ? 'Adding...' : 'Add to Cart'}
</Text>
</AddItemToOrder.Button>
) : (
<AddItemToOrder.Quantity.Root min={0} max={10} step={1}>
<AddItemToOrder.Quantity.Decrement>
<Text>-</Text>
</AddItemToOrder.Quantity.Decrement>
<AddItemToOrder.Quantity.Input />
<AddItemToOrder.Quantity.Increment>
<Text>+</Text>
</AddItemToOrder.Quantity.Increment>
</AddItemToOrder.Quantity.Root>
)}
{error && (
<Text>
{error.message}
</Text>
)}
</View>
)}
</AddItemToOrder.Root>
)
}
Made with ❤️ by Haus Tech Team