Invoice history
ProThe account's invoices: number, when each was issued, the period it covers, the amount in its own currency, its status (an open or overdue one says when it's due), and a download your app serves. Candor never builds the PDF or takes payment: pass portal and the footer points at the billing provider's own portal (Polar, Stripe, Paddle) for cards and payment methods.
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
First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/invoice-history
Brings in: badge, data, heading-level, heading-scope, locale, ops, page-state, table, tokens, utils · npm: lucide-react@^1.67.0
Usage
"use client"
import { InvoiceHistory } from "@/components/candor/invoice-history"
export default function Example() {
return (
<InvoiceHistory
className="max-w-3xl"
portal={{ provider: "Stripe", href: "#billing-portal" }}
invoices={[
{ id: "i4", number: "INV-2026-0011", issuedAt: "2026-11-01", periodStart: "2026-11-01", periodEnd: "2026-11-30", amount: 588, currency: "USD", status: "open", dueAt: "2026-11-15" },
{ id: "i3", number: "INV-2026-0010", issuedAt: "2026-10-01", periodStart: "2026-10-01", periodEnd: "2026-10-31", amount: 588, currency: "USD", status: "paid", url: "#invoice-0010.pdf" },
{ id: "i2", number: "INV-2026-0009", issuedAt: "2026-09-01", periodStart: "2026-09-01", periodEnd: "2026-09-30", amount: 490, currency: "USD", status: "refunded", url: "#invoice-0009.pdf" },
{ id: "i1", number: "INV-2026-0008", issuedAt: "2026-08-01", periodStart: "2026-08-01", periodEnd: "2026-08-31", amount: 490, currency: "USD", status: "overdue", dueAt: "2026-08-15", url: "#invoice-0008.pdf" },
]}
/>
)
}<InvoiceHistory>
| Prop | Type | Default | Description |
|---|---|---|---|
| invoicesrequired | Invoice[] | — | The invoices, newest first as the app orders them. |
| portal | { provider: string; href: string } | undefined | — | The billing provider's customer portal, where payment methods are changed: its name and the link your server made. |
| state | ListState | undefined | — | What the list is showing: its rows (ready, the default), loading, empty or error with the reason, or partial with a note over the rows. Loading, empty and error draw in place of the rows, in this component's own words, so a list the app doesn't have yet isn't shown as an empty one. |
| onRetry | (() => void) | undefined | — | Shows Try again when `state` is an error; called when it's pressed. Pass it only when it loads the list again. |
| className | string | undefined | — | Extra classes for the outer element. |
| headingLevel | HeadingLevel | undefined | — | The level of the list'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 invoiceHistory in <LocaleProvider strings={{ … }}>; the table is exported as invoiceHistoryStrings.
| Key | English |
|---|---|
| label | Invoices |
| heading | Invoices |
| empty | No invoices yet. |
| loading | Loading invoices |
| error | Invoices couldn’t be loaded. |
| invoice | Invoice |
| issued | Issued |
| period | Period |
| periodRange | {start} to {end} |
| amount | Amount |
| status | Status |
| download | Download |
| downloadInvoice | Download invoice {number} |
| notReady | Not ready |
| notReadyWhy | The PDF for {number} isn’t ready yet. |
| paid | Paid |
| open | Open |
| overdue | Overdue |
| void | Void |
| refunded | Refunded |
| due | due {date} |
| portal | Payment methods and billing are managed at {provider}. |
| openPortal | Open {provider} |
Types
- Invoice
- One invoice as the billing provider issued it.
{ id: string; /** The invoice's number as printed on it, e.g. "INV-2026-0042". */ number: string; issuedAt: string; periodStart: string; periodEnd: string; /** The total in major units: 49.5 for $49.50. */ amount: number; /** ISO 4217 code, e.g. "USD" or "EUR". */ currency: string; status: "paid" | "open" | "overdue" | "void" | "refunded"; /** When an open or overdue invoice is due. */ dueAt?: string | undefined; /** Where your app serves the PDF (your route, or the provider's hosted link). Without it the row says it isn't ready. */ url?: string | undefined; }
Types from other items
The props above take these. Each is defined by another item; import it from the path shown.
- ListState from @/lib/candor/data
- What a list or table is showing, the same shape as `ChartState` and as page-state's `ListState` (which the free tier ships, so it declares its own rather than import this file): its rows (ready), the shape of rows still coming (loading), nothing and why (empty), the rows it has with a note on what's missing (partial), or why it couldn't load (error). Lists without a `state` show their rows.
ChartState- 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- ChartState from @/lib/candor/data
{ kind: "ready"; } | { kind: "loading"; } /** Nothing to plot, and why. */ | { kind: "empty"; reason: string; } /** Some of the range is missing. */ | { kind: "partial"; note: string; } | { kind: "error"; reason: string; }