Skip to content
Tier

Billing details

Pro

The details printed on invoices, as a form: billing email, the name and tax ID on invoices, and the postal address. There is no card field: cards and payment methods belong at the billing provider's portal (Polar, Stripe), which invoice-history and subscription-status link to. Errors from your server show under the field they concern; Save stays locked, saying which field is missing, until the required ones are filled. "Saved" shows once a save resolves, until the next edit.

Markdown
Version 5.2.0· other versions

This page is for 5.2.0, the current release. An earlier release’s page is the Markdown that release published, so the docs match the version you have installed (candor-lock.json records it).

Not in the docs of 1.3.0, 1.4.0, 1.5.0, 1.5.1, 1.6.0, 1.7.0, 1.8.0, 1.8.1, 1.9.0, 1.9.1, 2.0.0, 2.1.0, 2.2.0, 2.3.0, 2.4.0, 2.5.0, 2.5.1, 2.6.0, 2.6.1 and 2.6.2: the page came later, or had another name then.

Docs pages weren’t kept for 1.0.0, 1.0.1, 1.1.0, 1.1.1, 1.2.0 and 1.2.1: they are kept from 1.3.0 on, and before that only llms.txt was published.

Preview

Loading the preview

Install

With the Candor CLI

First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/billing-details

Brings in: button, combobox, heading-level, heading-scope, input, locale, locked, tokens, utils · npm: lucide-react@^1.67.0

Usage

TSX · The example above
"use client"

import * as React from "react"
import { BillingDetails, type BillingField } from "@/components/candor/billing-details"

export default function Example() {
  const [errors, setErrors] = React.useState<Partial<Record<BillingField, string>>>()
  return (
    <BillingDetails
      className="max-w-lg"
      taxIdLabel="VAT number"
      countries={[
        { code: "GB", name: "United Kingdom" },
        { code: "IE", name: "Ireland" },
        { code: "US", name: "United States" },
      ]}
      value={{ email: "accounts@keel.example", name: "Keel Ltd", line1: "1 Harbour Street", city: "Bristol", postalCode: "BS1 4AA", country: "GB" }}
      errors={errors}
      onSave={async (d) => {
        await new Promise((r) => setTimeout(r, 400))
        if (d.taxId && !/^GB\d{9}$/.test(d.taxId)) {
          setErrors({ taxId: "That isn’t a UK VAT number: GB and nine digits." })
          throw new Error("invalid")
        }
        setErrors(undefined)
      }}
    />
  )
}

<BillingDetails>

PropTypeDefaultDescription
valuePartial<BillingDetailsValue> | undefined—The details on file. Without it the form starts empty.
onSaverequired(details: BillingDetailsValue) => void | Promise<void>—Called with the details; the button waits on a returned promise and shows Saved when it resolves.
countries{ code: string; name: string }[] | undefined—Countries to choose from, by ISO code and name. Without it, country is a text field.
taxIdLabelstring | undefined—What the tax ID is called where the account is, e.g. "VAT number" or "GSTIN". Defaults to "Tax ID".
errorsPartial<Record<BillingField, string>> | undefined—Your server's errors, by field, shown under each field.
errorstring | undefined—An error that isn't about one field, in the app's words.
busyboolean | undefinedfalseThe app is saving: the button says so.
classNamestring | undefined—Extra classes for the outer element.
headingLevelHeadingLevel | undefined—The level of the form's heading, so it fits under the page's own headings. Default: one below the nearest `HeadingScope`, else 2 (`<h2>`).

Strings

The fixed words, in English by default. Change any of them under billingDetails in <LocaleProvider strings={{ … }}>; the table is exported as billingDetailsStrings.

KeyEnglish
lockedSavingSaving the details
headingBilling details
introWhat appears on your invoices. Cards and payment methods are managed with the billing provider, not here.
emailBilling email
emailHintInvoices and payment receipts go here.
nameName on invoices
namePlaceholderCompany or person
taxIdTax ID
optional{label} (optional)
addressAddress
line1Address line 1
line2Address line 2
cityCity
regionState, province or region
postalCodePostal code
countryCountry or region
countryPlaceholderChoose a country or region
needsFill in {label}.
saveSave details
savingSaving…
savedSaved
saveFailedSaving the details failed.

Types

BillingDetailsValue
The details printed on invoices.
{ email: string; name: string; taxId?: string | undefined; line1: string; line2?: string | undefined; city: string; region?: string | undefined; postalCode?: string | undefined; /** An ISO 3166 code ("GB") when `countries` is given, else what the person typed. */ country: string; }
BillingField
keyof BillingDetailsValue

Types from other items

The props above take these. Each is defined by another item; import it from the path shown.

HeadingLevel from @/lib/candor/heading-level
A heading's level in the page's outline: `1` is `<h1>`, `6` is `<h6>`. Every Candor item that draws a heading takes its `headingLevel` as this type; the level is where the heading sits in the outline, never how large it looks.
1 | 2 | 3 | 4 | 5 | 6