@get-asset/react
Bills
Styled, drop-in accounts payable — bill date, description, vendor, due date, a status tag, the balance and what settled it, one row per bill. The header carries a status filter and a “Create new bill” button that reveals a form above the table; clicking a row expands a reconciliation of what it is worth, what has been matched and what that leaves, and the kebab offers the full view, a status change and delete. Covers loading, error, empty and success, and restacks onto a narrow layout via a container query.
Built on the headless Bills compound from @get-asset/sdk.
Preview
Loading preview…
Required scopes
The access token used by AssetProvider must include these scopes for this primitive to fetch data successfully:
bill:readbill:createbill:updatebill:deletevendor:readvendor:createledger:readtransaction:readexternal_account:read
API reference
Root props
| Prop | Type | Default | Description |
|---|---|---|---|
dateFrom | CalendarDateString | — | Inclusive start (YYYY-MM-DD) of a fixed due-date window. Setting it detaches the list from the provider's shared range entirely; must be paired with dateTo (one without the other throws). Use periodDateRange to derive the pair from a month, quarter, or year. |
dateTo | CalendarDateString | — | Inclusive end (YYYY-MM-DD) of a fixed due-date window. Must be paired with dateFrom. Omit both to follow the provider's DatePicker-driven range. |
pageSize | number | 25 | Rows per request. Further pages load from the footer's Load more button. |
defaultStatusFilter | BillStatus | "all" | "all" | Which status the filter starts on. "all" shows every bill; open, paid and voided are the available statuses. |
defaultSelectedId | string | — | Expand this bill's row on mount, for deep-linking straight to a bill instead of making the user find it. |
defaultViewingId | string | — | Open this bill's document view on mount — the deep link for “show me this bill”, where defaultSelectedIdis “open its row in the list”. See The document view. |
defaultCreating | boolean | false | Open the new-bill panel on mount. |
hideCreate | boolean | false | Hides the Create new bill button. For tenants that raise bills in another system and push them in through the API — the list is then a record, not a place to author one. |
bills | BillOverride[] | — | Override the fetched bills (skips the list request). The status filter and period are not re-applied, so supply the rows you want shown. |
vendors | VendorOption[] | — | Override the vendor directory. Resolves each row's Vendor cell and fills the form's picker — pass it alongside bills to stay offline. |
ledgers | LedgerOption[] | — | Override the categories offered on a line of the new-bill form. |
matchCandidates | MatchCandidateOverride[] | — | Override the transactions the match picker offers, for previews and tests. Live, the panel queries them itself once a row with a balance is expanded — pass this alongside bills to keep an offline story offline. An empty array is still an override, and shows the picker with nothing in it. See Matching. |
intake | { onCreated?: (id: string | null) => void } | — | Lets a bill be raised from a document. Supplying it puts an Upload bill control in the header; without it the header is create-only. See Ingestion. |
state | "loading" | "error" | "empty" | — | Force a specific state for testing. |
hasMore | boolean | — | Force the Load more button whenever the list isn't being fetched (previews/tests). |
className | string | — | Forwarded to the outer Card wrapper element. |
States
Loading preview…
Loading preview…
Loading preview…
Loading preview…
Loading preview…
Loading preview…
Loading preview…
Loading preview…
Behaviour
The bill number leads neither the table nor its header: it is an internal reference that reads as noise next to a description and a vendor. It remains on the full view. The date takes its place, because that is what the list is ordered by.
The header names the period beside the title, and the table holds the bills falling due inside it — the range comes from the AssetProvider, as it does on the report cards, so the list moves with the rest of the dashboard. dateFrom and dateTopin a fixed window for this card alone, detaching it from the provider's range. See Period and status filter for the window it sends.
Clicking a row expands it — the row itself is the toggle, not just the kebab. What opens is a reconciliation rather than a second copy of the row: the bill total, what has been matched to it, and what that leaves. The matched line only appears once something has been matched. When a bank transaction is larger than the balance, the API allocates what is due and records the excess as vendor credit, so the bill reads Settled in full. The line items live on the full view instead, which is the surface for reading the bill whole.
The Transactionscolumn names what settled the row — the bank's own description, with +Nonce more than one transaction settles it, and a dash when nothing has. It is content, not a control: one click anywhere on the row (the kebab aside) opens it. Each cell's text carries a text cursor at its own width, and a drag that selects it never opens the row — the click handler stands down whenever a selection ends the click. See Matching.
The kebab carries the full view, the status changes and delete. Editing lives on the full view, and reaches the lines alone — the number, vendor and dates are fixed at creation. Open, paid and voided are the complete status set, and every transition is a plain Bill PATCH. See Statuses.
The new-bill form takes a number, a description, a vendor, two dates and any number of lines with an optional category each, and totals the lines as they are typed. A vendor who is not in the directory yet can be added from inside the form, without losing the bill being drafted. Opening the full view or the form hides the list rather than pushing it down.
Every value, control and gate is a slot on the headless compound. See the Bills part inventoryto rearrange the layout, swap the primitives, or add the parts this layout leaves out — the per-payment list and a detached detail panel.
The document view
The full view reads the bill as a document, on the layout every document source shares — an invoice, a receipt or a statement opens into the same anatomy, with only the source-specific copy changing. A back link returns to the list; the header carries the vendor's name, the document date, a Bill tag and the status as a plain tag beside it — the same badge the table's Status column wears; a summary band states what the document claims and settles it; and the record's own card holds the dates, the line items and the amount due. Delete and Edit share the action bar at the foot.
Editing swaps the line rows — and only them — for inputs, each with its own remove control, with the amount due following the keystrokes; Save sends what changed and Cancel discards it. The dates stay read-only: they are fixed at creation. + Add Item under the lines is the same editor, opened with a fresh line ready to type into. See Editing: lines only.
The summary band leads with a sentence — who you owe, how much, due when — and carries the matching surface under it: one chip per matched payment, a Select Transactions picker, and a payment total that goes red when more is attached than the bill is worth. See Matching.
The line items sit behind a toggle that names what it hides (“Show 14 Items”). They start open — the lines are what the document is made of — and stay put across an edit; the toggle is there for the reader who wants the totals without the itemisation.
A bill raised from an uploaded file opens as a split view: the details keep the left column, and the source document renders beside them — the file on a page card, a control to open the original in its own tab, and zoom for images (a PDF renders through the browser's viewer, which brings its own). The header also wears the document's processing status as a small dotted chip — the same vocabulary as the AssetDocuments list. The lookup behind all of this is the headless DocumentViewer.
A bill with no source document — one raised here, or pushed in through the API — keeps the single column at full width, with no status chip and no empty pane beside it.
Ingestion
Pass intake and the header gains an Upload bill control. Picking a document hands it to Asset, which reads it and raises the bill from it — party, lines and journal entry included. A banner under the header narrates it: uploading, then reading, then the record it added. When it finishes the list refreshes, the new record's row opens, and onCreatedfires with its id.
Nothing is asked of the reader: the extraction is the platform's. A failure gets its own line and stays until it is dismissed — an upload that quietly gave up is worse than one that visibly did — and every failure line ends on nothing was added, which is what settles whether a half-made record is now in the books. Try again appears only where running the same file could end differently: a file that is too large, the wrong type, or already on record will be all of those next time too. Reading runs on the server, so a reload picks the progress back up rather than losing it.
Matching
Beside the reconciliation sits Payments: one chip per payment already matched to the bill, and a Select Transactions picker to add one. It is AssetTransactionMatch wired to the open row; the document view carries the same one inside its summary band, on the inline layout.
The picker is a searchable multi-select over the bank transactions around the bill's due date, closest amount first: the date, the description and the amount on three fixed tracks, so a dozen rows can be scanned by eye. Ticking one records the payment and unticking it takes the payment off again — each is its own request, so there is no save. Already-matched transactions come back ticked, and every chip carries its own remove control, which is the way back if the wrong one was attached. Transactions moving money the wrong way are never offered: Guardrails has why.
A settled bill keeps the panel and the picker, disabled — you can still read and undo what paid it, and hiding the control would make a settled row look like a different component. It also costs nothing: nothing is outstanding, so no candidates are fetched. See Settled records.
In the table, the row itself is the way in — one click opens it at the matching panel, whatever cell the click lands on. matchCandidates is the override that keeps a preview or a test off the network, and TransactionMatch is the headless compound to compose against for a layout of your own.