Cart screen
ProReact Native · ExpoA shopping cart: line items with a quantity stepper whose minus turns into remove, swipe left to remove or save for later, a promo code field, a free delivery meter and totals that roll with every change above a checkout bar. Removing an item offers Undo; an empty cart shows its own empty state.
Installation
$ npx shadcn@latest add @goodui-native-pro/cart-screenThen run npx expo install --fix so react-native-reanimated, react-native-svg match your Expo SDK. Files land in components/goodui-native/cart-screen.
Pro screens install from the license-gated @goodui-native-pro registry. Setup: Pro access.
- Wrap the app in GestureHandlerRootView and ThemeProvider, and render <Toaster /> once for the Undo toast.
Usage
// app/cart.tsx
import { Stack, router } from "expo-router";
import { CartScreen } from "@/components/goodui-native/cart-screen/cart-screen";
import { api, useCart } from "@/lib/cart";
export default function CartRoute() {
const cart = useCart();
return (
<>
<Stack.Screen options={{ headerShown: false }} />
<CartScreen
items={cart.items} // [{ id, title, variant, price, quantity, image }]
onItemsChange={cart.setItems}
currency="EUR"
shippingFee={4.9}
freeShippingOver={100}
onApplyPromo={(code) => api.checkPromo(code)} // resolve { code, label, percentOff } or reject new Error("message")
onCheckout={(summary) => router.push("/checkout")}
onSaveForLater={(item) => api.saveForLater(item.id)}
onBrowse={() => router.push("/shop")}
onBack={() => router.back()}
/>
</>
);
}When to use
- The cart or basket of a shop, grocery or food ordering app before checkout.
- Any list of things being bought where quantities and a running total matter.
- Picking delivery and paying: use checkout-screen after this one.
- A wishlist without quantities or totals: use list-row rows with swipe-actions.
Examples
On its own, with sample items (try the code WELCOME10)
<CartScreen />Always charge delivery, no undo toast
<CartScreen items={items} onItemsChange={setItems} freeShippingOver={Infinity} undo={false} />API reference
| Prop | Type | Default |
|---|---|---|
| items Items (controlled). Pair with `onItemsChange`. | CartItem[] | — |
| defaultItems Starting items when uncontrolled. | CartItem[] | defaultItems |
| onItemsChange Called with the new list after a quantity change or a removal. | (items: CartItem[]) => void | — |
| currency ISO 4217 code for every amount. Default "EUR". | string | "EUR" |
| shippingFee Delivery fee below `freeShippingOver`. Default 4.9. | number | 4.9 |
| freeShippingOver Subtotal (after discount) from which delivery is free. Default 100. Pass `Infinity` to always charge. | number | 100 |
| onApplyPromo Check a promo code. Resolve with the promo, or reject with an Error whose message is shown under the field. | (code: string) => Promise<CartPromo> | defaultApplyPromo |
| defaultPromo Promo applied when the screen opens. | CartPromo | null | null |
| onCheckout Checkout button. Receives the totals. Return a promise to show a spinner on the button. | (summary: CartSummary) => unknown | — |
| onSaveForLater The second swipe action, "Save for later". Hidden when omitted. The item leaves the cart. | (item: CartItem) => void | — |
| onBrowse Empty state action, for example navigating to the shop. | () => unknown | — |
| onBack Back button. Hidden when omitted. | () => void | — |
| undo Undo toast after a removal. Default `true`; needs the Toaster at the app root. | boolean | true |
| locale BCP 47 locale for amounts. Defaults to the device locale. | string | — |
| labels Overrides for the built-in strings; a GooduiLocaleProvider pack sets them app-wide. | Partial<CartScreenLabels> | — |
| style Style for the outer container. | StyleProp<ViewStyle> | — |
| testID Identifier for end-to-end tests. | string | — |
Motion and haptics
- Every total rolls: the line price when its quantity changes, the subtotal, discount, delivery and total, and the checkout bar.
- The free delivery meter fills on the smooth spring and turns green when the order qualifies.
- Swipe left: the actions unfold with the finger; a long swipe removes, the row slides out and collapses, and the rows below close the gap on the smooth spring.
- A wrong code shakes the field and slides the error in; a good one swaps the field for the code chip.
- Reduced motion: digits change in place, the meter jumps, rows close the gap at once, and the field does not shake.
- Haptic: tick on each quantity step, delete when the trash removes the item, warning at the limits (QuantityStepper)
- Haptic: select when a swipe crosses the full-swipe threshold, delete when the remove action runs (SwipeActions); notify for the toast, undo on its Undo
- Haptic: tap on Apply and Check out, success or error when Apply settles (Button); error when the field shows an error (TextField)
- Haptic: tap on removing the promo chip
- Quantities, swipes and rolls animate on the UI thread; React renders on item, promo and keyboard changes.
- Rows render in a ScrollView: built for the tens of items a cart holds.
Accessibility
- 'Cart' is the header with the item count next to it.
- Each row's stepper is adjustable and named 'Quantity, <title>'; at one, minus becomes a labelled remove button.
- Swipe actions are also screen reader actions on the row (Remove, Save); a removal is announced, and Undo is in a toast.
- The promo field announces its error under the field; an applied code is announced and shown as a chip with a remove button.
- Totals are read as plain numbers; the free delivery line is visible text ('Add €12.40 more for free delivery').
Notes for AI
- Controlled: pass items and onItemsChange (called after every quantity change and removal). Uncontrolled: pass defaultItems.
- onApplyPromo resolves with { code, label, percentOff } or { code, label, amountOff }, or rejects with an Error whose message is shown under the field. The default accepts WELCOME10 only, for previews.
- Delivery is free when the subtotal after discount reaches freeShippingOver; pass Infinity to always charge shippingFee.
- The Undo toast needs <Toaster /> once at the app root; pass undo={false} if you have no Toaster.
- Render it full-bleed with the navigator header hidden (headerShown: false). It handles the safe areas and lifts the promo field above the keyboard on native, hiding the checkout bar while typing.
Related
The last step before paying: delivery address and payment method pickers, delivery options, the order summary with totals that roll when the delivery changes, and slide to pay in thumb reach. Paying covers the screen with a success check and confetti, then hands over through onDone.
A shop's product detail: swipeable gallery with page pills, title, rating and a price that rolls with the size, color swatches or chips, size chips, a quantity stepper, specs, a reviews summary and a bottom bar with the rolling total. When Add to cart succeeds the product photo flies in an arc into the cart button and its count pops.
A − value + stepper whose value rolls in the direction of change. Hold a button to repeat with acceleration; the ends push back with a rubber-band bounce and a warning haptic. In a cart the − turns into a trash button at the minimum.
Row actions that unfold with the finger; a long swipe lets the edge action take over and run, and destructive actions collapse the row before it leaves.
A number whose digits roll in the direction of change, with digits that open and close in place and currency signs that glide instead of jumping.
A designed empty, error or offline state: a tinted disc pops in, its line art draws itself stroke by stroke with the highlight landing last, then the title, description and actions rise in.