shadcn.io is not affiliated with official shadcn/ui
Exchange Flow Checkout Block for Radix UI
An exchange sheet shown open over a swimwear shop's orders page: the item as it is, three swaps in a radio group, a difference worked out in cents (you pay $6.00, even, or $4.00 comes back) and a switch to keep the change as store credit.
Installation
Overview
Let people swap an item without a return and a reorder, with this exchange flow for React with Radix UI. It opens from the order it belongs to: the orders page of Kelpie Goods, a small swimwear shop, has an outline Exchange Button at its header's end, and the page, a drawn order and two more under a soft scrim, sits inside a hairline frame with a plus mark on each corner. The Sheet shows the item as it is (a Ripple one-piece, $64.00), three swaps as radio rows (native radios, so the arrow keys move between them) with a drawn tile, a name and a price each, and the difference worked out in whole cents: "You pay $6.00", "Even exchange" or "$4.00 comes back". When the swap costs less, a Switch chooses between a refund to the card and keeping the change as store credit; its place is kept when it is not needed. Confirm exchange is the only Shine Button and closes the sheet with a note that nothing was charged, refunded or shipped. For the sheet shown open for preview, the header button opens the real Sheet with the same state.
FAQ
Why is the sheet already open, and what do I keep?
So you can see it without a click. On load the sheet is a copy drawn in place at the end of the frame: a native dialog element that isn't modal, so nothing is trapped, hidden or focused. The Exchange button opens the real Sheet, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same ExchangeBody and ExchangeFoot and share the block's state. In your app keep the Sheet and drop the copy: the "in-place" state, the scrim and the dialog.
How is the difference worked out?
diffOf subtracts the item's price from the swap's, in whole cents. A positive result is what you pay, zero is an even exchange and a negative one is what comes back. Each swap's row shows its own difference ("+ $6.00", "Same price", "− $4.00") so you can compare before choosing.
What does the store credit switch do?
It appears for a swap that costs less. Off, the change goes back to the card in three to five working days; on, it is kept as store credit, ready when the swap ships. For other swaps its slot is invisible and inert, so the sheet does not change height, and the sentence under it explains where the money goes in every case.
Which side does the sheet open from?
From the end of the page's reading direction: the right in a left-to-right page and the left in a right-to-left one. The Sheet's own sides are physical, so sideOf reads the page's direction when it opens. The copy shown on load uses logical positions, and on a phone it docks to the foot of the frame as a bottom sheet.
What happens after Confirm exchange?
The sheet closes, focus returns to the Exchange button and a status line says the exchange was requested in this demo. The drawn order behind it changes its tag to "Exchange requested" and its tile to the new product, so the result is visible once the sheet is gone.
What stays local in the demo, and how do I wire it up?
Everything: the item, the swaps and their prices are typed constants, and nothing is charged, refunded or shipped. In your app load the order line and its eligible swaps (same or other products, with live stock) from your order API, post the chosen swap and the credit choice on Confirm, and let the response set the new order state.