useCartActions()
New in v4.0.0 - Form-based cart actions using React 19's useActionState for progressive enhancement and better error handling.
Overview
useCartActions() provides form-friendly cart operations that work with HTML forms and support progressive enhancement. Unlike useShoppingCart() which uses imperative methods (onClick), this hook returns actions designed for the action attribute of forms.
When to Use
Use useCartActions() when:
- Building forms for cart operations
- You want progressive enhancement (works without JS)
- You need built-in loading states
- Working with Next.js App Router server actions
Use useShoppingCart() or useOptimisticCart() when:
- Using onClick handlers
- Simple button interactions
- Programmatic cart updates
- You don't need form-based interactions
Requirements
- React 19.0.0 or higher
- use-shopping-cart v4.0.0 or higher
Basic Usage
import { useCartActions } from 'use-shopping-cart'
function ProductCard({ product }) {
const { addToCartAction, isAddingItem } = useCartActions()
return (
<form action={addToCartAction}>
<input type="hidden" name="product" value={JSON.stringify(product)} />
<button disabled={isAddingItem}>
{isAddingItem ? 'Adding...' : 'Add to Cart'}
</button>
</form>
)
}API
Returned Values
Add Item
addToCartAction: Form action for adding itemsaddItemState:{ status, error, productId }isAddingItem: Boolean pending state
Form fields:
product(required): JSON string of product object (max 50,000 chars)count(optional): Positive integer count to add (default: 1)
<form action={addToCartAction}>
<input type="hidden" name="product" value={JSON.stringify(product)} />
<input type="number" name="count" defaultValue="1" />
<button>Add to Cart</button>
</form>Remove Item
removeFromCartAction: Form action for removing itemsremoveItemState:{ status, error, itemId }isRemovingItem: Boolean pending state
Form fields:
itemId(required): ID of the item to remove
<form action={removeFromCartAction}>
<input type="hidden" name="itemId" value={item.id} />
<button>Remove</button>
</form>Update Quantity
updateQuantityAction: Form action for setting quantityupdateQuantityState:{ status, error, itemId, quantity }isUpdatingQuantity: Boolean pending state
Form fields:
itemId(required): ID of the itemquantity(required): Non-negative integer quantity
<form action={updateQuantityAction}>
<input type="hidden" name="itemId" value={item.id} />
<input type="number" name="quantity" defaultValue={item.quantity} />
<button>Update</button>
</form>Increment Item
incrementItemAction: Form action for incrementingincrementItemState:{ status, error, itemId }isIncrementingItem: Boolean pending state
Form fields:
itemId(required): ID of the itemcount(optional): Positive integer amount to increment (default: 1)
Decrement Item
decrementItemAction: Form action for decrementingdecrementItemState:{ status, error, itemId }isDecrementingItem: Boolean pending state
Form fields:
itemId(required): ID of the itemcount(optional): Positive integer amount to decrement (default: 1)
Clear Cart
clearCartAction: Form action for clearing cartclearCartState:{ status, error }isClearingCart: Boolean pending state
Global State
isPending:trueif ANY action is in progress
Examples
Product Card with Form
function ProductCard({ product }) {
const { addToCartAction, addItemState, isAddingItem } = useCartActions()
return (
<div className="product">
<h3>{product.name}</h3>
<p>${product.price / 100}</p>
<form action={addToCartAction}>
<input type="hidden" name="product" value={JSON.stringify(product)} />
<label>
Quantity:
<input type="number" name="count" min="1" defaultValue="1" />
</label>
<button disabled={isAddingItem}>
{isAddingItem ? 'Adding...' : 'Add to Cart'}
</button>
{addItemState.status === 'success' && (
<p className="success">✓ Added to cart!</p>
)}
{addItemState.error && <p className="error">{addItemState.error}</p>}
</form>
</div>
)
}Quantity Controls
function CartItem({ item }) {
const {
incrementItemAction,
decrementItemAction,
removeFromCartAction,
isPending
} = useCartActions()
return (
<div className="cart-item">
<h4>{item.name}</h4>
<div className="quantity-controls">
<form action={decrementItemAction}>
<input type="hidden" name="itemId" value={item.id} />
<button disabled={isPending}>-</button>
</form>
<span>{item.quantity}</span>
<form action={incrementItemAction}>
<input type="hidden" name="itemId" value={item.id} />
<button disabled={isPending}>+</button>
</form>
</div>
<form action={removeFromCartAction}>
<input type="hidden" name="itemId" value={item.id} />
<button disabled={isPending}>Remove</button>
</form>
</div>
)
}Progressive Enhancement Example
This form works even without JavaScript loaded!
function ProductCard({ product }) {
const { addToCartAction, isAddingItem } = useCartActions()
return (
<form action={addToCartAction}>
<input type="hidden" name="product" value={JSON.stringify(product)} />
{/* Works without JS! */}
<button type="submit" disabled={isAddingItem}>
{isAddingItem ? 'Adding...' : 'Add to Cart'}
</button>
</form>
)
}Comparison: Actions vs Methods
onClick Handler (use useShoppingCart or useOptimisticCart)
function ProductCard({ product }) {
const { addItem } = useShoppingCart()
return <button onClick={() => addItem(product)}>Add to Cart</button>
}Best for:
- Simple button clicks
- Programmatic updates
- Imperative code
- Quick implementations
Form Action (use useCartActions)
function ProductCard({ product }) {
const { addToCartAction, isAddingItem } = useCartActions()
return (
<form action={addToCartAction}>
<input type="hidden" name="product" value={JSON.stringify(product)} />
<button disabled={isAddingItem}>
{isAddingItem ? 'Adding...' : 'Add to Cart'}
</button>
</form>
)
}Best for:
- Progressive enhancement
- Next.js App Router
- Accessibility
- SEO-friendly interactions
- Works without JavaScript
Error Handling
All actions return state with error handling:
function ProductCard({ product }) {
const { addToCartAction, addItemState, isAddingItem } = useCartActions()
return (
<form action={addToCartAction}>
<input type="hidden" name="product" value={JSON.stringify(product)} />
<button disabled={isAddingItem}>
{isAddingItem ? 'Adding...' : 'Add to Cart'}
</button>
{/* Error display */}
{addItemState.status === 'error' && (
<div className="error-message">
<strong>Error:</strong> {addItemState.error}
</div>
)}
{/* Success feedback */}
{addItemState.status === 'success' && (
<div className="success-message">Item added successfully!</div>
)}
</form>
)
}TypeScript
import { useCartActions } from 'use-shopping-cart'
import type { Product } from 'use-shopping-cart/core'
function ProductCard({ product }: { product: Product }) {
const { addToCartAction, addItemState, isAddingItem } = useCartActions()
return (
<form action={addToCartAction}>
<input type="hidden" name="product" value={JSON.stringify(product)} />
<button disabled={isAddingItem}>
{isAddingItem ? 'Adding...' : 'Add to Cart'}
</button>
</form>
)
}Benefits
✅ Progressive Enhancement - Works without JavaScript
✅ Built-in Loading States - No manual state management
✅ Error Handling - Automatic error capture and display
✅ Accessibility - Better keyboard and screen reader support
✅ SEO Friendly - Forms work before JS loads
✅ Next.js Ready - Integrates with App Router patterns
Interactive Demo
Cart Display
Total Items: 0
Cart is empty. Add items to see them here!
Bananas
Yummy yellow fruit
$4.00
Try Form-Based Actions:
💡 What's happening?
- Uses HTML forms (progressive enhancement)
- Automatic loading states with useActionState
- Built-in error handling and success feedback
- Works without JavaScript enabled
Try the form-based actions above! Notice how they provide automatic loading states and error handling without any extra state management code.
See Also
- useShoppingCart() - Imperative cart methods
- useOptimisticCart() - Optimistic updates
- React 19 useActionState - Official docs
