Staaarter

Billing Coupon Code

A compact coupon code entry block for checkout pages and plan upgrade modals. Typing a code and clicking Apply moves through a loading state, then either an applied state that shows the matched coupon and its calculated discount, or an invalid state with an inline error. Built with TypeScript, shadcn/ui Input and Button components, Lucide icons, and Tailwind CSS.

By Staaarter Team
Billing
Updated May 24, 2026
Docs
Live preview

Docs

Installation

A real shadcn registry command, not a copy-paste stand-in: it fetches this block plus its button, input, label dependencies and any missing npm packages, and installs them straight into your project.

npx shadcn add https://staaarter.com/r/billing-coupon-code.json

Prefer not to use the CLI? Copy the source from the Code toggle above into src/components/blocks/billing/billing6.tsx instead.

Usage

The file also exports billing6Demo, the exact props behind the preview above. Spread it to get a working section in one line, then replace it with your own data.

import { Billing6, billing6Demo } from "@/components/blocks/billing/billing6";

export default function Page() {
  return <Billing6 {...billing6Demo} />;
}

Props

PropTypeDefaultDescription
headingstring"Have a coupon?"Block heading.
descriptionstringnoneSupporting description under the heading.
placeholderstring"Enter code"Placeholder text for the code input.
applyLabelstring"Apply"Label for the apply button when idle.
subtotalnumber49Order subtotal in dollars, used to compute the dollar value shown for percent-based coupons.
validCouponsCouponDefinition[]noneCoupons the block will accept, matched case-insensitively against the typed code.
classNamestringnoneExtra classes for the outer section element.

Types

interface CouponDefinition {
  code: string;
  label: string;
  type: "percent" | "fixed";
  value: number;
}

Behavior notes

  • Clicking Apply with a non-empty code moves the button into a loading state (spinning icon, both the input and button disabled) for 700ms before resolving, simulating a server round trip.
  • After the simulated delay the typed code is matched case-insensitively against validCoupons. A match replaces the form with an applied summary row showing the code and its calculated discount, using subtotal for percent-based coupons.
  • No match sets an invalid state that shows a destructive-colored message under the input, the input stays editable so the user can immediately retry with a different code.
  • The applied summary has its own remove button that clears the applied coupon, the typed code, and returns to the idle input, so the user can try a different code without reloading the block.
  • The pending timeout from a submit is cleared on unmount and on manual removal, so a delayed 'applied' or 'invalid' result can never land after the component is gone or after the user has already cleared the field.

Frequently asked questions