shadcn.io is not affiliated with official shadcn/ui
Payment Method Dialog Block for React
A ticket shop's order summary with its payment-method dialog shown open: a saved card, a bank transfer and an invoice as rows, each asking only for the one detail it needs, and a Pay with row behind the dialog that changes when you choose.
Installation
Overview
Let people pick how to pay, with this payment-method dialog for React with Base UI. It opens from the order summary of Gildhall, a ticket shop: the page's header holds an outline Change payment method Button, and the page itself, three ticket rows, the totals and a Pay with row, is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The Dialog lists three ways to pay as radio rows with an icon tile each: a saved Visa ending 4242, a bank transfer and an invoice. Under the rows sits the one detail the chosen method needs: the card asks only for its three-digit security code in an Input, the bank transfer shows a reference to copy with a Copy Button and how long it takes, and the invoice asks for a purchase-order number. A Checkbox makes the choice the default. The only Shine Button follows the choice (Use Bank transfer), and choosing changes the Pay with row behind the dialog and the note under it. For preview the dialog is drawn open in place when the page loads; the header's button opens the real dialog, with the same rows and the same state. For checkouts, invoices and any app with more than one way to pay.
FAQ
Why is the dialog already open, and what do I keep?
So you can see it without a click. On load the dialog is a copy drawn in place over the page: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The Change payment method button opens the real Dialog, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same MethodFields component and share the block's state. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.
Is it safe to ask for a security code here?
Not in your app. This dialog never takes a card number, and the code is read in memory and dropped when the dialog closes: it is not kept in state after Use. A real checkout should never let a card number or a security code touch your own page or server. Take both with your payment provider's hosted fields, which draw them in frames the provider controls and keep card data out of your page and out of your PCI scope, and show the saved card the provider returns in the row. The rows, the choice, the default box and the details for the other two methods stay as they are.
What does each method ask for?
The detail for the chosen method only; the other two stay in the same cell, invisible and inert, so the dialog keeps its height whichever you choose. The card needs exactly three digits (anything else is dropped as you type, and an empty or short code is refused with its own sentence and focus on the field). The bank transfer needs nothing from the person: it shows the reference and what to send. The invoice needs a purchase-order number of 3 to 20 letters, digits or dashes. Once a message shows, it follows your typing and clears when the field is right.
What stays local?
Everything. Nothing is charged: Use only changes the Pay with row and its note behind the dialog and says, in the polite status, what was chosen and that nothing was charged. The saved card, the bank reference and the totals are typed in the file. In your app send the method to your payments API, show its result in the same place, and take the reference from the response. Copy uses the real clipboard and says whether it worked.
Is it accessible from the keyboard?
Yes. The three methods are one native radio group, so the arrow keys move between them and the checked row is the one Tab reaches; the dialog is named by its title and described by its sentence, focus starts on the chosen method and stays inside until it closes, and Escape, Cancel and the close button return focus to Change payment method. Each field's hint or message is tied to it with aria-describedby. The trigger is passed to DialogTrigger through render, and the dialog is controlled with open and onOpenChange.
Related
- Pro
Add Card Dialog
Payments and plansA transit wallet's payment methods page with its add-card dialog shown open: a card drawn in greys that fills in as you type, a number that groups itself and names its network, checks on the number, expiry and code, and an Add card button that keeps only the last four digits.
- Pro
Split Payment Dialog
Payments and plansA point-of-sale ticket with its split-payment dialog shown open: methods added from a select, an amount for each, a running total with a bar by method, and Pay held back until the remainder is exactly zero.
- Pro
Select Plan Dialog
Payments and plansA static-site host's Sites page with its plan chooser shown open: three plans as rows that unfold to their features, a yearly switch that rewrites the prices and what is due today, and a comparison table behind a disclosure.
- Pro
Apply Coupon Dialog
Payments and plansA cookware shop's basket with its promo-code dialog shown open: one field that judges five typed codes with five different answers, applied codes kept as removable chips, and a summary that is worked out again after every change.
- Pro
Payment Method Billing
Payment and invoicesPayment methods in app settings: the card on file with an expiry warning, a backup bank account, a button that sets the default, and a dialog that checks and updates the card.
- Pro
Recurring Payment Dialog
Payments and plansA rent page with its autopay dialog shown open: an amount, how often, a start day from a folded inline calendar, when it ends, and the next four payment dates worked out under the fields.