A coupon input with Apply, loading, valid and invalid states. A valid code turns into a removable chip with the saving; an invalid one shakes.
Checkout / Promotions
npx shadcn@latest add https://hyperblur.dev/r/pay-coupon-field.jsonWorks in any React project set up with the shadcn CLI. The file lands in components/hyperblur/pay-coupon-field.tsx.
import { PayCouponField } from "@/components/hyperblur/pay-coupon-field"
<PayCouponField validate={(code) => code === "SPRING20" ? { valid: true, label: "20% off" } : false} onApply={(code) => applyCode(code)} />| Prop | Type | Default | Description |
|---|---|---|---|
| validate | (code: string) => boolean | { valid: boolean; label?: string; message?: string } | Promise<...> | accepts "SAVE10" | Checks a code. Return a result object to set the saving label or a custom error message. |
| value | string | Controlled code text. Pair with onChange. | |
| defaultValue | string | "" | Initial code when uncontrolled. |
| onChange | (code: string) => void | Called when the text changes. Codes are uppercased. | |
| onApply | (code: string, result: PayCouponResult) => void | Called once a code validates. | |
| onRemove | () => void | Called when the applied code is removed. | |
| placeholder | string | "Discount code" | Placeholder and accessible name of the input. |
| applyLabel | string | "Apply" | Text of the apply button. |
| invalidMessage | string | "That code is not valid or has expired." | Error shown when validate gives no message. |
| minLoadingMs | number | 700 | Shortest time the loading state shows, so fast checks do not flicker. |
| accent | string | "#2f7bff" | Focus ring color. |