# Transaction Ledger

> An animated transaction ledger for React and shadcn, grouped by day with each day's net.

Transactions grouped by day under sticky headers with each day's net. A pending row that settles washes once while its status swaps, rows are recategorised in place from a menu, and new rows rise once at the top.

- Tier: Eased Pro
- Interaction: Scroll
- Page: https://eased.dev/components/transaction-ledger
- Installs: motion, @base-ui/react

## Install

Transaction Ledger ships with Eased Pro (https://eased.dev/#pricing). Members add the `@eased-pro` registry with their token (https://eased.dev/account), then run:

```bash
npx shadcn add @eased-pro/transaction-ledger
```

Transactions grouped by day under sticky headers, each with its net total.

```tsx
import { TransactionLedger } from "@/components/eased/transaction-ledger/transaction-ledger";
import { useLiveTimeline } from "@/components/eased/_primitives/timeline-core/timeline-core";

const feed = useLiveTimeline(transactions);
<TransactionLedger transactions={feed.events} entering={feed.entering} now={Date.now()} categories={categories} onCategoryChange={recategorise} />
```

## Props

| Prop | Type | Default | |
|---|---|---|---|
| `transactions` | `TimelineEvent<LedgerTxn>[]` | | timeline-core events; `data` is `{ amount, currency, category, merchant, status }` (negative is money out). |
| `now` | `number` | | "Today" / "Yesterday" are relative to it. |
| `categories` | `string[]` | | Offered in each row's menu. |
| `onCategoryChange` | `(id, category) => void` | | |
| `filter` | `"all" \| "in" \| "out"` | `"all"` | |
| `entering` | `(id) => number \| null` | | From `useLiveTimeline`; new rows rise once. |
| `baseCurrency` | `string` | `"USD"` | Currency of each day's net. |
| `locale` / `timeZone` | `string` | runtime | |

Put it in a scroll region; headers stick to its top. Rows share one grid, so amounts and statuses keep one right edge.

**Motion signature:** a pending row that settles washes once while its status swaps with a blur; a new row rises 6px at the top and nothing else moves; the category menu grows from its chip. Filters apply instantly (rule 84). Reduced motion: fades only.
