Coupon Input

Cleans and checks a coupon code and says exactly why it didn't apply, so shoppers don't give up on a code that should work.

StopsValid coupon code rejected

Installation

pnpm dlx shadcn@latest add https://bollard-ui.vercel.app/r/coupon-input.json

The files land in your project. They're yours to edit.

Usage

coupon-input-usage.tsx
import { CouponInput, type CouponResult } from "@/components/ui/coupon-input"

<CouponInput
  onApply={check}
  defaultCode="save1OO"
  knownCodes={["SAVE100", "WELCOME20", "FEST10"]}
  timeZone="Asia/Kolkata"
/>

What it catches

  • Pasted spaces, hidden characters and wrong case
  • Look-alike characters (O/0, I/1) against known codes
  • The specific reason a code was rejected, such as the amount still needed
  • A capped discount, shown as the real saving
  • A worse code replacing a better one

Props

CouponInput props
PropTypeDefault
onApply* (required)

Checks a cleaned code against the cart. May return a Promise; a rejection shows a retry.

(code: string) => CouponResult | Promise<CouponResult>—
value

The applied code, controlled. `null` when none.

Applied | null—
defaultValue

The applied code when uncontrolled.

Applied | nullnull
onValueChange

Called when a code is applied or removed.

(value: Applied | null) => void—
knownCodes

Public codes (banners, emails) to compare a not-found code against for look-alike typos.

string[][]
defaultCode

A code to check straight away, e.g. from a `?coupon=` link or a "copy code" banner.

string—
caseSensitive

Keep letter case as typed. Most shops treat codes as case-insensitive.

booleanfalse
guardDowngrade

Ask before replacing an applied code with one that saves less. Turn off when codes stack.

booleantrue
currency

ISO 4217 code for savings and minimums.

string"INR"
locale

BCP 47 locale for money and dates.

string"en-IN"
timeZone

Time zone for expiry dates.

string—
messages

Reword or translate any sentence.

Partial<typeof COUPON_INPUT_MESSAGES>—

* required. Also accepts the native props of <div> (className, aria-*, data-*…), passed through to it.

Accessibility

  • Visible <label>; aria-invalid and aria-describedby to the status
  • Enter applies; suggestion and remove buttons have aria-labels
  • Focus returns to the input after actions

Only what the source does. Test with your own screen reader and content before shipping.

Limitations

This is a UX guard rail. Enforce on the server: Validate and price coupons on the server; never trust a discount computed in the browser.

  • Look-alike suggestions need knownCodes; otherwise it only cleans
  • Validity and rejection reasons come from your onApply

What it stops

Valid coupon code rejected

Also known as

coupon · promo code · discount code · voucher · offer code · checkout · cart · apply code

Field type

Dependencies

Pairs well with