shadcn.io is not affiliated with official shadcn/ui
Select Plan Dialog Block for React
A 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.
Installation
Overview
Let people choose a plan without leaving the page, with this plan chooser for React with Base UI. It opens from the Sites page of Kilnstack, a static-site host: the page's header holds an outline Choose a plan Button, and the page itself, a banner saying which plan the account is on and a list of deploys, is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The Dialog lists three plans as radio rows you choose by opening them: the chosen row unfolds to four feature lines with check marks, and the others stay a name, a pitch and a price. A Switch pays yearly (ten months for twelve) and rewrites every price, and one line says what is due today and when the plan renews. A disclosure holds a five-row table that compares all three plans. The one Shine Button follows the choice: Continue with Builder, or Keep Starter for the plan you are on. Continuing changes the banner behind the dialog. 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 pricing choices inside an app, freemium upgrades and any product with tiers.
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 Choose a plan 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 PlanFields component and share the block's state, so a plan chosen in the copy is there in the real one. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.
How do the rows open, and does the dialog change size?
Each plan is a native radio inside a label, so the browser gives you the group, the arrow keys and the checked state with no script. The chosen row's feature list is in a grid row that grows from zero height to its full height (the animation is off under reduced motion), and the closed lists stay in the markup, folded and inert, so they can't take focus. Every list has four short items, so the rows add up to the same height whichever plan is open; the comparison table is the one thing that makes the body longer, and it scrolls inside the dialog.
Where do the prices and the due-today line come from?
Each plan has a monthly price in cents; yearly is that times 10, shown as a year, and the free plan is zero either way. The due-today figure and the renewal date come from the chosen plan and the switch (a month or a year after the date the block treats as now), formatted with Intl.NumberFormat in US dollars and Intl.DateTimeFormat in UTC, so they read the same on the server and in every time zone. Replace the typed PLANS with the plans your billing provider returns, and take the due-today figure from its invoice preview, which knows about tax and credit.
What stays local?
Everything. Continuing only changes the banner behind the dialog and the sentence in the page header, and says in the polite status that nothing was bought. No plan is purchased and no card is charged. In your app send the plan and the cycle to your server and show its result in the same place; this dialog collects no payment details, so pair it with a payment method step.
Is it accessible from the keyboard?
Yes. The real dialog is named by its title and described by its sentence, focus starts on the chosen plan and stays inside until it closes, and Escape, Cancel and the close button return focus to Choose a plan. The arrow keys move between plans, the switch and the disclosure are real controls with names, and the table has a caption, column headers and row headers, with a word for each tick or dash. What is said while the dialog is open (a plan chosen, the cycle changed) is said inside it, because a modal hides the page's own status. The trigger is passed to DialogTrigger through render, and the dialog is controlled with open and onOpenChange.
How does it look on a phone?
The copy shown on load docks to the foot of the page as a bottom sheet, its two buttons stacked at the bottom with the primary on top, and the plans scroll above them. The feature lists become a single column and the comparison table keeps its four columns at 320 pixels. The real dialog keeps the Dialog's own phone layout, a sheet stuck to the bottom of the screen.
Related
- Pro
Upgrade Plan Dialog
Payments and plansA product-analytics usage page with its upgrade dialog shown open: your plan and the next joined by an arrow, a yearly switch, and a table of limits with what you have now and what you would have after.
- Pro
Manage Subscription Dialog
Side sheetsA design tool's account page with its subscription sheet shown open: the plan, billing cycle and seats editable in place, a next-invoice foot worked out from the controls, and a plan card behind it that changes when you save.
- Pro
Payment Method Dialog
Payments and plansA 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.
- Pro
Checkout Plan Picker Billing
Upgrade and downgradeThe plan picker inside a checkout: plan cards, a monthly or yearly period, a seat stepper, a promo code that validates itself and an order summary whose total rolls as you change anything.
- Pro
Plan Comparison Settings Billing
Upgrade and downgradeEvery plan side by side inside app settings: a table with the account's own plan marked and tinted, one change button per plan, a row for when each change takes effect, and plan tabs on a phone.
- Pro
Cancel Subscription Dialog
Destructive confirmsA backup service's plan page with its exit flow shown open: an alert dialog in three steps, why you are leaving, an offer matched to the reason, and what ends and when, with a destructive final button and a thin progress bar.