KQ-1332 · CART/DRAWER

Cart Drawer

A bag that slides over the page it belongs to: the panel arrives from the right edge on glide over a fading scrim and can be pulled shut by its header, 1:1 with a rubber-banded overpull and a release or flick that closes on glide with the throw's velocity. Each line's face slides over a tray of actions — Save and Remove, or in full mode a pull right through that removes it — a removed line leaves on the exit ease while its height closes on glide and Undo keeps it for six seconds, steppers roll their counts on snap, and the totals roll, count or cross-fade as anything changes. A bar fills toward free delivery on glide with a van on its leading edge, and when the visitor carries the subtotal over the threshold the bar turns success, flecks spray from its end and the news lands on recoil; Escape closes a tray then the drawer, and every swipe has a button that does the same thing.

KQ-1332 · CART/DRAWER

3 items · €51.00 · €9.00 away from free delivery

Install

One command — the source lands in your repo. Or copy it from the Code tab.

$ pnpm dlx shadcn@latest add @kinetiq/cart-drawer

Props

items / defaultItems
CartLine[]
default — / defaultCartItems
Controlled or initial lines: name, variant, unit price, compare-at price, quantity, max, and a thumbnail or drawn art.
onItemsChange
(items: CartLine[]) => void
default —
Fires from the stepper, swipe, button, Undo or upsell that changed the bag, with every line.
open / defaultOpen
boolean
default — / false
Controlled or initial: the drawer is open.
onOpenChange
(open: boolean) => void
default —
Fires from the close button, Escape, the scrim or the pull that closed it.
upsells
CartUpsell[]
default defaultCartUpsells
Things that pair with the bag, offered in a sideways row; ones already in the bag are left out.
onUpsellAdd
(upsell: CartUpsell) => void
default —
An upsell was added; it is already at the top of the lines.
onRemove
(line: CartLine) => void
default —
A line left the bag: swiped, removed, or stepped below one.
onUndo
(line: CartLine) => void
default —
Undo put a removed line back where it was.
onSaveForLater
(line: CartLine) => void
default —
Save for later was chosen from a line's tray; it has left the bag.
onCheckout
(items: CartLine[], total: number) => void | Promise<void>
default —
Checkout was pressed. A promise holds the button pending; a rejection's message is shown.
threshold
number
default 60
The subtotal that earns free delivery, in the currency's major unit.
shipping
number
default 4.95
Delivery under the threshold.
swipe
"reveal" | "full" | "off"
default "reveal"
How a line is pulled away: open a tray of actions, pull right through to remove, or not at all.
totals
"roll" | "count" | "fade"
default "roll"
How changing figures move: digits that roll, figures that count through the cents, or a cross-fade.
format
(amount: number) => string
default euros
Formats an amount for display.
title
string
default "Your bag"
The drawer's heading and accessible name.
checkoutLabel
string
default "Checkout"
The checkout button's words.
emptyTitle
string
default "Your bag is empty"
The empty bag's heading.
emptyBody
string
default —
The empty bag's line.
onContinue
() => void
default —
Continue shopping was pressed on the empty bag (it also closes the drawer).
status
"ready" | "loading" | "error"
default "ready"
Loading draws placeholder lines; error offers Retry.
onRetry
() => void
default —
The Retry button of the error state.
children
ReactNode
default —
The page the drawer opens over; it is inert while the drawer is open.
label
string
default —
The dialog's accessible name when it should differ from the title.
sound
boolean
default false
Pops on the steppers and upsells, a brighter one when free delivery unlocks and a low one as a full swipe arms; swishes as the drawer and lines come and go.
disabled
boolean
default false
Shows the bag but takes no input.
className
string
default —
Classes for the box the drawer lives in; give it a height.