Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Apply Coupon Dialog Block for React

A 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.

Framework
UI library

Preview

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Overview

Let shoppers add a discount without leaving the basket, with this promo-code dialog for React with Base UI. It opens from the basket of Kettlecraft, an enamel cookware shop: the page's header holds an outline Add a promo code Button, and the basket itself, three drawn pieces and an order summary, sits under a soft scrim inside a hairline frame with a plus mark on each corner. The dialog is one Field with an Input that judges what you type against five codes with five different answers: one applies, one applies and stacks with it, one has expired, one was already used on the account, and one needs a bigger basket and says by exactly how much. Each failure is its own sentence under the field. A code that works stays on the order as a Badge chip with what it takes off and a button to take it back, and the summary under it, subtotal, discounts, shipping and total, is worked out again after every change; the basket behind the dialog prints the same totals. Apply code is the only Shine Button. For preview the dialog is drawn open in place when the page loads; the header button opens the real Dialog, with the same field and the same applied codes. For carts, checkouts, subscriptions and any order that takes a code.

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 basket: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The Add a promo code 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 CouponFields component and share the block's state, so a code typed in the copy is there in the real one and so are the codes already applied. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.

Where do the five answers come from?

From CODES and the small function problemWith, both in the file. A code is unknown, already on the order, a second percentage code, expired (its last day is compared with the block's AS_OF), already used on the account, or short of its minimum spend, checked in that order, and the message names the code and says what to do, such as "Add $14.00 more to use HEAVY5." One percentage code per order is a rule of problemWith; free shipping stacks with it. In your app send the code with the cart to your server and show the reason it returns in the same slot; keep the sentences specific, since a bare "Invalid code" gives people nothing to fix.

How is the summary worked out?

By totalsOf, a pure function of the codes applied. Prices are whole cents, so nothing drifts: a percentage code takes its share of the subtotal rounded to a cent, a fixed code takes its amount, free shipping removes the shipping, and the total is the subtotal minus the discounts plus the shipping. The dialog and the basket behind it call the same function, so they never disagree, and each price is one left-to-right run inside a right-to-left page. Replace the cart constants with your own, and let your server stay the source of truth when it prices the order for real.

Does applying a code charge or use it up?

No. Here an applied code is only state in the block, and the polite status message says what it took off and what the total is now, ending with "Nothing was charged in this demo." Reload and the codes are gone. In your app apply the code on your server in apply, which should also reserve it against the order, and show its answer: the chip and the totals from the response, or the reason in the field's error line with the dialog still open.

Is it accessible from the keyboard?

Yes. The real dialog is named by its title and described by its sentence, focus starts in the code field and stays inside until it closes, and Escape, the close button and Cancel (Done once a code is applied) all return focus to the Add a promo code button. The trigger is passed to DialogTrigger through render, and the dialog is controlled with open and onOpenChange. Applying keeps the dialog open so you can stack a second code, with focus back in the field; each chip's remove button names its code, and one polite status message reports what changed. The copy shown on load moves no focus; Escape inside it and its buttons close it and put focus on the header button.

How does it look on a phone?

The copy shown on load docks to the foot of the page as a bottom sheet, its actions pinned at the bottom with Apply code on top, and the field, the chips and the summary scroll above them. The basket behind it drops its drawn order summary, since the dialog shows the totals. The real dialog keeps the Dialog's own phone layout, a sheet stuck to the bottom of the screen.

  • Pro

    Gift Card Dialog

    Payments and plans

    A café's gift cards page with its builder dialog shown open: a card drawn in greys that reads as you build it, preset or custom amounts, three printed patterns, a recipient and a message, and delivery now or on a day picked in an inline calendar.

  • Pro

    Guest Checkout Dialog

    Payments and plans

    A cart with its identify-step dialog shown open: Guest, Sign in and Create account swap the fields under a segmented control, one button names the mode, and the cart's stepper moves on to Shipping once you continue.

  • Pro

    Add Card Dialog

    Payments and plans

    A 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

    Billing Address Dialog

    Payments and plans

    An invoice page with its billing-address dialog shown open: the form relabels, reorders and revalidates for the United States, the United Kingdom, Germany and Japan, a switch fills the company's address, and the address is printed the way an envelope would carry it.

  • Pro

    Add to Cart Dialog

    Payments and plans

    A stool shop's product page with its add-to-cart dialog shown open: two heights that redraw the stool and move the price, three finishes with a sentence each, a quantity that stops at the stock left, and a total that follows the choices.

  • Pro

    Payment Method Dialog

    Payments and plans

    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.