DEV Community

Ruthel Cedrick Ascens BALOCK
Ruthel Cedrick Ascens BALOCK

Posted on

I built a headless hook, ref API & ledger view for my React accounting component

react-accounting-diary v2.3.0: headless hook, imperative ref API, ledger view, validation callbacks, category/tags, JSON import/export.


TL;DR

react-accounting-diary is a lightweight React component for generating accounting diaries with export to PNG, JPEG, PDF, CSV, Excel, and JSON.

v2.3.0 adds 6 major features that turn it from a UI component into a full accounting toolkit. Here's what changed.

Live Demo →


What's new in v2.3.0

1. Headless hook — useAccountingDiary

The biggest addition. Build your own UI while leveraging the full data layer:

import { useAccountingDiary } from 'react-accounting-diary';

function CustomDiary() {
  const {
    data,
    addTransaction,
    editTransaction,
    deleteTransaction,
    undo, redo, canUndo, canRedo,
    totals,
    accountSummary,
    importJSON, exportJSON,
  } = useAccountingDiary({
    initialData: [],
    onChange: (data) => saveToBackend(data),
    onBeforeAdd: (item) => item.amount > 0,
    onBeforeDelete: (item) => confirm(`Delete "${item.text}"?`),
  });

  return (
    <div>
      <p>Debit: {totals.debit} | Credit: {totals.credit}</p>
      <p>{totals.isBalanced ? '✓ Balanced' : '⚠ Unbalanced'}</p>
      <button onClick={() => addTransaction({
        date: '2024-01-01', text: 'Rent', isDebit: true,
        amount: 1000, account: 'Rent', currency: 'USD',
      })}>Add</button>
      <button onClick={undo} disabled={!canUndo}>Undo</button>
      <button onClick={redo} disabled={!canRedo}>Redo</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

You get: add/edit/delete with validation, undo/redo, computed totals, per-account summary, JSON import/export — all without rendering a single component.

2. Imperative ref API

Control the component programmatically from a parent:

import AccountingDiary from 'react-accounting-diary';
import { useRef } from 'react';

function App() {
  const ref = useRef(null);

  return (
    <>
      <button onClick={() => ref.current?.exportToPDF()}>PDF</button>
      <button onClick={() => ref.current?.exportToJSON()}>JSON</button>
      <button onClick={() => ref.current?.addTransaction({
        date: '2024-01-01', text: 'Salary', isDebit: true,
        amount: 5000, account: 'Payroll', currency: 'USD',
      })}>Add via Ref</button>
      <button onClick={() => console.log(ref.current?.getTotals())}>Totals</button>
      <button onClick={() => console.log(ref.current?.getAccountSummary())}>Accounts</button>
      <AccountingDiary ref={ref} title="My Company" columnHeader={true} />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

13 methods available: exportToPNG, exportToJPEG, exportToPDF, exportToCSV, exportToExcel, exportToJSON, importJSON, addTransaction, undo, redo, getData, getTotals, getAccountSummary.

3. Validation callbacks — onBefore*

Intercept and block mutations before they happen:

<AccountingDiary
  data={data}
  onBeforeAdd={(item) => {
    if (item.amount > 100000) {
      alert('Amount too high!');
      return false; // blocks the add
    }
    return true;
  }}
  onBeforeEdit={(old, updated) => confirm(`Change ${old.account}?`)}
  onBeforeDelete={(item) => confirm(`Delete "${item.text}"?`)}
  onChange={setData}
/>
Enter fullscreen mode Exit fullscreen mode

Supports both sync (boolean) and async (Promise<boolean>) — perfect for server-side validation.

4. Ledger view

Toggle between diary view (grouped by date) and ledger view (grouped by account):

<AccountingDiary data={data} showLedgerToggle={true} columnHeader={true} />
Enter fullscreen mode Exit fullscreen mode

The ledger view shows:

  • Transactions grouped by account name
  • Running balance per account
  • Per-account debit/credit totals
  • Color-coded balances (green for debit, red for credit)

5. Category & tags

Classify transactions with optional fields:

const data = [
  {
    date: '2024-01-01',
    text: 'Office rent',
    isDebit: true,
    amount: 2000,
    account: 'Rent',
    currency: 'USD',
    category: 'Operating',
    tags: ['monthly', 'fixed'],
  },
];
Enter fullscreen mode Exit fullscreen mode

Both are searchable via the search filter. The add/edit dialog now includes category and tags fields.

6. JSON import/export

In addition to CSV and Excel:

  • Toolbar: JSON import button in the toolbar
  • Ref API: ref.current.exportToJSON() / ref.current.importJSON(jsonString)
  • Hook: exportJSON() / importJSON(jsonString)

Quick recap — all features

  • ✅ Headless hook (useAccountingDiary)
  • ✅ Imperative ref API (13 methods)
  • ✅ Validation callbacks (onBeforeAdd, onBeforeEdit, onBeforeDelete)
  • ✅ Ledger view with running balance
  • ✅ Category & tags (searchable)
  • ✅ JSON import/export
  • ✅ Export to PNG, JPEG, PDF, CSV, Excel, JSON
  • ✅ Import from CSV and JSON
  • ✅ Callback props (onChange, onAdd, onEdit, onDelete)
  • ✅ i18n / Localisation (36+ label keys)
  • ✅ Sortable columns, pagination, grand total
  • ✅ Undo/Redo, dark mode, keyboard navigation
  • ✅ ~21KB gzipped, zero external CSS

Install

npm install react-accounting-diary
Enter fullscreen mode Exit fullscreen mode

Minimal example

import AccountingDiary from 'react-accounting-diary';

function App() {
  return (
    <AccountingDiary
      title="My Company"
      columnHeader={true}
      showGrandTotal={true}
      showLedgerToggle={true}
      pageSize={10}
      onChange={(data) => console.log(data)}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Numbers

  • Bundle: ~21KB gzipped (ESM)
  • React: 16.8+ / 17 / 18 / 19
  • TypeScript: full support with exported types
  • Zero external CSS

What's next

  • [ ] Transaction templates (rent, salary — reusable presets)
  • [ ] Drag & drop import (CSV/JSON files onto the component)
  • [ ] Filter dropdown by account/category
  • [ ] Mini chart — debit/credit visualization by month
  • [ ] Multi-journal support (purchases, sales, bank, cash)

Live Demo: react-accounting-diary-demo.vercel.app
GitHub: ruthel/react-accounting-diary
npm: react-accounting-diary

If this is useful, a ⭐ on GitHub helps a lot. Feedback and PRs welcome.

Top comments (0)