Invoice list
Install
npx shadcn add @klarhimmel/app-billing-invoicesUsage
import { Theme } from '@/components/klarhimmel/theme/style';
import { BlockSurface } from '@/components/klarhimmel/blocks/surface';
import AppBillingInvoices from '@/components/klarhimmel/blocks/app-billing-invoices';
export default function Section() {
return (
<Theme theme="brand">
<BlockSurface>
<AppBillingInvoices props={{
"eyebrow": "Billing",
"title": "Invoices",
"description": "Every invoice for Exempelhotellet. Download one as a PDF.",
"caption": "Invoices, newest first",
"columns": {
"number": "Invoice",
"date": "Date",
"amount": "Amount",
"status": "Status"
},
"rows": [
{
"number": "INV-1042",
"date": "1 Jun 2026",
"amount": "490 kr",
"status": "due",
"href": "#inv-1042"
},
{
"number": "INV-1031",
"date": "1 May 2026",
"amount": "490 kr",
"status": "paid",
"href": "#inv-1031"
},
{
"number": "INV-1019",
"date": "1 Apr 2026",
"amount": "490 kr",
"status": "paid",
"href": "#inv-1019"
},
{
"number": "INV-1007",
"date": "1 Mar 2026",
"amount": "490 kr",
"status": "failed",
"href": "#inv-1007"
}
],
"statusLabels": {
"paid": "Paid",
"due": "Due",
"failed": "Failed"
},
"download": "Download PDF",
"scrollHint": "Scroll sideways for more columns",
"recovery": {
"payAgain": "Pay again",
"updateCard": {
"label": "Update card",
"href": "#card"
},
"processing": "Processing",
"said": "Payment attempt started. We will email you the result."
},
"state": "filled",
"messages": {
"emptyTitle": "No invoices yet",
"emptyBody": "Your first invoice appears here at the end of the first billing month."
}
}} />
</BlockSurface>
</Theme>
);
}States
invoice list in 2 forms: filled, empty.
state: filled
Billing
Invoices
Every invoice for Exempelhotellet. Download one as a PDF.
Invoices, newest first Invoice Date Amount Status Download PDF INV-1042 1 Jun 2026 490 kr DueDownload PDFINV-1042 INV-1031 1 May 2026 490 kr PaidDownload PDFINV-1031 INV-1019 1 Apr 2026 490 kr PaidDownload PDFINV-1019 INV-1007 1 Mar 2026 490 kr FailedUpdate card INV-1007Download PDFINV-1007 Scroll sideways for more columns
Payment attempt started. We will email you the result.
Processing
No invoices yet
Your first invoice appears here at the end of the first billing month.
state: empty
Billing
Invoices
Every invoice for Exempelhotellet. Download one as a PDF.
Invoices, newest first Invoice Date Amount Status Download PDF INV-1042 1 Jun 2026 490 kr DueDownload PDFINV-1042 INV-1031 1 May 2026 490 kr PaidDownload PDFINV-1031 INV-1019 1 Apr 2026 490 kr PaidDownload PDFINV-1019 INV-1007 1 Mar 2026 490 kr FailedUpdate card INV-1007Download PDFINV-1007 Scroll sideways for more columns
Payment attempt started. We will email you the result.
Processing
No invoices yet
Your first invoice appears here at the end of the first billing month.
Notes
When to use it
A table of past invoices with status and a download link per row, for a billing page. For the current plan and usage use `app-billing-plan`; for a generic table of other data use `app-data-table`.
Writing for it
- Rows take 1 to 8 entries. Each has a number, date and amount of 30 characters at most, a status of paid, due or failed, and an `href` for the download file. - Set `download` (30) to a short verb like Download; the invoice number is appended for screen readers so the link is not just the same word eight times. - Use `recovery` only for failed rows: `payAgain` and an `updateCard` link appear there, and `said` (80) is the announcement after a retry.
Watch out for
Eight rows is the ceiling, so a long history needs `app-data-pagination` or a link elsewhere. In the empty state `messages.emptyTitle` is required, and the Pay again button only changes the row to processing locally; it does not charge anything.
Pairs well with
Usage
The block in a page, with its default content (long lists shortened). Every prop is in the table below.
import { Theme } from '@/components/klarhimmel/theme/style';
import { BlockSurface } from '@/components/klarhimmel/blocks/surface';
import AppBillingInvoices from '@/components/klarhimmel/blocks/app-billing-invoices';
export default function Section() {
return (
<Theme theme="brand">
<BlockSurface>
<AppBillingInvoices props={{
"eyebrow": "Billing",
"title": "Invoices",
"description": "Every invoice for Exempelhotellet. Download one as a PDF.",
"caption": "Invoices, newest first",
"columns": {
"number": "Invoice",
"date": "Date",
"amount": "Amount",
"status": "Status"
},
"rows": [
{
"number": "INV-1042",
"date": "1 Jun 2026",
"amount": "490 kr",
"status": "due",
"href": "#inv-1042"
}
],
"statusLabels": {
"paid": "Paid",
"due": "Due",
"failed": "Failed"
},
"download": "Download PDF",
"scrollHint": "Scroll sideways for more columns",
"recovery": {
"payAgain": "Pay again",
"updateCard": {
"label": "Update card",
"href": "#card"
},
"processing": "Processing",
"said": "Payment attempt started. We will email you the result."
},
"state": "filled",
"messages": {
"emptyTitle": "No invoices yet",
"emptyBody": "Your first invoice appears here at the end of the first billing month."
}
}} />
</BlockSurface>
</Theme>
);
}Props
The type, default and text limit of every prop. On a real page, over-limit text is kept and checkSections flags it (Installation, Check a page before you ship); this live preview shows the default until it fits. The panel above edits all props.
| Prop | Type | Default | Description |
|---|---|---|---|
eyebrow | string | 'Billing' | Text, up to 60 characters. Optional; empty is not rendered. |
title | string | 'Invoices' | Text, up to 120 characters. |
description | string | 'Every invoice for Exempelhotellet. …' | Text, up to 300 characters. Optional; empty is not rendered. |
caption | string | 'Invoices, newest first' | Text, up to 80 characters. |
columns | { number, date, amount, status } | {…} | Fields: number, date, amount, status. |
rows | { number, date, amount, status, href }[] | 4 items | List of 1 to 8. Each item: number, date, amount, status ('paid' | 'due' | 'failed'), href. |
statusLabels | { paid, due, failed } | {…} | Fields: paid, due, failed. |
download | string | 'Download PDF' | Text, up to 30 characters. |
scrollHint | string | 'Scroll sideways for more columns' | Text, up to 40 characters. |
recovery | { payAgain, updateCard, processing, said } | {…} | Fields: payAgain, updateCard, processing, said. |
state | 'filled' | 'empty' | 'filled' | One of 'filled', 'empty'. |
messages | { emptyTitle, emptyBody } | {…} | Fields: emptyTitle, emptyBody. |
headingLevel | '1' | '2' | - | One of '1', '2'. Optional; empty is not rendered. |
Install
Installs
app-billing-invoices- Badge
- ButtonLink
- EmptyState
- About
- A list of invoices as a table: number, date, amount, a status badge (paid, due, failed: always a word) and a download link named for its invoice that stays in view while the other columns scroll, and a failed invoice that can be paid…
- States
- Paid, due and failed rows, and an empty list. Every status is a word, every download link names its invoice.