Ledger Table

Double-entry lines with a running balance. Debits and credits get their own columns and are totalled separately, because a ledger where the two do not agree is the finding.

src/components/ui/ledger-table.tsx

About

From the component's own source, where it was written.

A double-entry ledger with a running balance.

The component checks that debits equal credits and says so when they do not. That is the entire point of double-entry — an unbalanced ledger is a bug, and a table that renders one without comment is worse than useless because it looks authoritative.

Amounts are integer minor units (cents), not floats. 0.1 + 0.2 is a genuinely wrong number in a ledger, and running balances accumulate that error line by line until the total is visibly off.

The running balance is computed here rather than taken per row, so it cannot disagree with the entries above it.

Install

npx astralyx-ui add ledger-table

Usage

import { LedgerTable } from '@/components/ui/ledger-table'

<LedgerTable entries={entries} openingBalance={0} />

Composer

Change the props and the source below follows.

DateDescriptionDebitCreditBalance
Invoice #2261 — NorthwindINV-2261US$4,800.00—US$17,300.00
Payment receivedINV-2261—US$4,800.00US$12,500.00
Cloud hostingAWS-0824US$964.12—US$13,464.12
Affiliate payout — batch 41PO-0041US$2,120.50—US$15,584.62
Refund — order 88213RF-88213—US$49.90US$15,534.72
TotalsUS$7,884.62US$4,849.90US$15,534.72

Debits and credits do not balance — out by US$3,034.72.

Props
tsx
<LedgerTable entries={entries} openingBalance={1250000} />

Examples

A running balance

API

Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.

LedgerTable

Extends ComponentProps<'div'>.

PropTypeDefaultDescription
entriesLedgerEntry[]required{ id, date, description, account?, reference?, debit?, credit? }. Exactly one of debit or credit per line.
currencystring'USD'—
localestring'en-GB'—
openingBalancenumber0Minor units. The running balance starts here rather than at zero.
showBalancebooleantrue—
columnLabels{ date?: ReactNode description?: ReactNode account?: ReactNode debit?: ReactNode credit?: ReactNode balance?: ReactNode }{ date: 'Date', description: 'Description', account: 'Account', debit: 'Debit', credit: 'Credit', balance: 'Balance', }Column headings.
totalsLabelReactNode'Totals'—

Types

LedgerEntry

A double-entry ledger with a running balance.

FieldTypeDescription
idstring—
dateDate—
descriptionReactNode—
account?ReactNode—
reference?ReactNode—
debit?numberMinor units. Exactly one of debit or credit per entry.
credit?number—

Behaviour

TopicDetailDescription
amountsminor unitsIntegers throughout. Running balances accumulate, and a float error accumulates with them.
totalscomputedDebit and credit columns are summed independently and shown in the footer, so an unbalanced set is visible rather than hidden in a net figure.