Skip to content

shadcn.io is an independent project built on shadcn/ui

Free

Checkout Form for React

A checkout form with contact email, card details, and a billing address, ending in Complete Purchase.

Payment Details holds three sections: Contact, Payment Method, and Billing Address. Expiry sits beside CVV, and city beside ZIP. The card number wants 16 digits, expiry wants MM/YY, and CVV wants 3 or 4 digits.

Use this when checkout asks for contact, card, and address together. If a provider hosts the card, keep the email on an Input. In React, Base UI draws the fields and Zod checks the patterns.

Preview

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.
Use email insteadNo credit card

Usage

<form className="w-full max-w-md" onSubmit={form.handleSubmit(onSubmit)}>
  <FieldGroup>
    <div className="flex flex-col">
      <h2 className="font-bold text-2xl">Payment Details</h2>
      <p className="text-muted-foreground text-sm">
        Complete your purchase
      </p>
    </div>
    <FieldSet>
      <FieldLegend>Contact</FieldLegend>
      <FieldGroup className="gap-4">
        <Controller
          control={form.control}
          name="email"
          render={({ field, fieldState }) => (
            <Field invalid={fieldState.invalid}>
              <FieldLabel htmlFor="checkout-form-email">Email</FieldLabel>
              <Input
                {...field}
                id="checkout-form-email"
                placeholder="you@example.com"
                type="email"
              />
              <FieldDescription>
                We&apos;ll send your receipt here.
              </FieldDescription>
              <FieldError>{fieldState.error?.message}</FieldError>
            </Field>
          )}
        />
      </FieldGroup>
{/* The rest of the file is under Installation. */}

FAQ

What card number does the schema accept?

Exactly 16 digits: cardNumber must match /^\d{16}$/, with the message "Card number must be 16 digits". The input also has maxLength={16}, and spaces fail the pattern.

What do expiry and CVV check?

Expiry must look like MM/YY, /^\d{2}\/\d{2}$/ with "Format: MM/YY"; it doesn't check the month or that the date is in the future. CVV must be 3 or 4 digits, with "CVV must be 3 or 4 digits" and maxLength={4}.

What do the address fields check?

Address is .min(5), city .min(2) and ZIP code a 5-digit or ZIP+4 pattern; name on card is .min(2) and email z.email(). These use Zod's default messages.

What happens if country is left on the placeholder?

country starts as "" and must pass .min(1, "Please select a country."), so Complete Purchase shows that message until United States, United Kingdom, Canada or Australia is picked.

How are the sections arranged?

A Payment Details heading, then three FieldSets with a FieldLegend and a FieldGroup each. The email has the FieldDescription "We'll send your receipt here." Expiry and CVV, and city and ZIP code, are in grid grid-cols-2 gap-4 rows.

What does Complete Purchase receive?

Complete Purchase is a full-width type="submit" button. form.handleSubmit(onSubmit) logs all nine values to the console once the schema passes. Never send raw card numbers to your own server; use your payment provider's SDK.

API Reference

Field

Sets invalid={fieldState.invalid}, htmlFor="checkout-form-email", htmlFor="checkout-form-cardName", htmlFor="checkout-form-cardNumber", htmlFor="checkout-form-expiryDate" and htmlFor="checkout-form-cvv". The rest is on the Field page.

Input

Sets field, id="checkout-form-email", placeholder="you@example.com", type="email", id="checkout-form-cardName" and placeholder="John Doe". The rest is on the Input page.

Select

Sets items={countries}, name={field.name}, onValueChange, next, id="checkout-form-country" and placeholder="Select country". The rest is on the Select page.

Button

Sets type="submit". The rest is on the Button page.