Skip to content
All Taste components

Forms

Billing details form

A two-column billing form with GSTIN and address that folds to one column on narrow screens.

2 columns · 1 column when narrow · optional fields · phone prefix

Billing details

These appear on every invoice we send you.

+91
import { cn } from "@/lib/utils";

const input =
  "h-9 w-full rounded border border-border-strong bg-bg px-3 text-sm text-fg outline-none transition placeholder:text-fg-subtle focus:border-primary focus:ring-[3px] focus:ring-primary/20";

function Field({ label, optional, help, className, children }:
  { label: string; optional?: boolean; help?: string; className?: string; children: React.ReactNode }) {
  return (
    <label className={cn("grid gap-1.5", className)}>
      <span className="text-[13px] font-medium text-fg">
        {label} {optional && <span className="font-normal text-fg-subtle">(optional)</span>}
      </span>
      {children}
      {help && <span className="text-xs text-fg-subtle">{help}</span>}
    </label>
  );
}

export function BillingDetailsForm({ onSubmit }: { onSubmit: (data: FormData) => void }) {
  return (
    <form
      onSubmit={(e) => { e.preventDefault(); onSubmit(new FormData(e.currentTarget)); }}
      className="w-full max-w-[640px] rounded-lg border border-border bg-bg p-5"
    >
      <div className="mb-5">
        <h3 className="text-base font-medium tracking-tight text-fg">Billing details</h3>
        <p className="mt-0.5 text-[13px] text-fg-subtle">These appear on every invoice we send you.</p>
      </div>

      <div className="grid grid-cols-1 gap-x-3 gap-y-4 sm:grid-cols-2">
        <Field label="Full name"><input name="name" required autoComplete="name" className={input} /></Field>
        <Field label="Company" optional><input name="company" autoComplete="organization" className={input} /></Field>
        <Field label="Email"><input name="email" type="email" required autoComplete="email" className={input} /></Field>
        <Field label="Phone">
          <span className="flex h-9 overflow-hidden rounded border border-border-strong bg-bg transition focus-within:border-primary focus-within:ring-[3px] focus-within:ring-primary/20">
            <span className="flex items-center border-r border-border bg-surface px-3 text-sm text-fg-muted">+91</span>
            <input name="phone" type="tel" required autoComplete="tel-national" className="min-w-0 flex-1 bg-transparent px-3 text-sm text-fg outline-none" />
          </span>
        </Field>
        <Field label="GSTIN" optional help="15 characters, from your GST certificate. Leave blank if you aren’t registered." className="sm:col-span-2">
          <input name="gstin" maxLength={15} pattern="[0-9A-Z]{15}" className={cn(input, "uppercase tabular-nums tracking-wide")} />
        </Field>
        <Field label="Address" className="sm:col-span-2"><input name="address" required autoComplete="street-address" className={input} /></Field>
        <Field label="City"><input name="city" required autoComplete="address-level2" className={input} /></Field>
        <Field label="PIN code"><input name="pin" required inputMode="numeric" pattern="[0-9]{6}" autoComplete="postal-code" className={input} /></Field>
        <Field label="State">
          <select name="state" defaultValue="KA" className={cn(input, "appearance-none")}>
            <option value="KA">Karnataka</option><option value="KL">Kerala</option><option value="MH">Maharashtra</option><option value="TN">Tamil Nadu</option>
          </select>
        </Field>
        <Field label="Country">
          <select name="country" defaultValue="IN" className={cn(input, "appearance-none")}>
            <option value="IN">India</option><option value="AE">United Arab Emirates</option><option value="SG">Singapore</option>
          </select>
        </Field>
      </div>

      <div className="mt-5 flex flex-wrap items-center justify-between gap-3 border-t border-border pt-4">
        <label className="inline-flex cursor-pointer items-center gap-2 text-sm text-fg">
          <input type="checkbox" name="shipSame" defaultChecked className="size-4 accent-primary" /> Ship to this address
        </label>
        <div className="flex gap-2">
          <button type="reset" className="h-9 rounded px-3.5 text-sm font-medium text-fg transition hover:bg-surface-2">Cancel</button>
          <button type="submit" className="h-9 rounded bg-primary px-3.5 text-sm font-medium text-primary-fg transition hover:bg-primary-hover">Save details</button>
        </div>
      </div>
    </form>
  );
}

The code uses the Taste Tailwind preset. Make yours on the Taste page with Export my system.