affectUI › Blocks › Billing

Invoice & Credit Note

An invoice and its credit note stacked like paper – switch between them, pick lines to credit, adjust amounts and issue.

Part of affectUI Pro. Install: npx shadcn@latest add https://www.affectui.com/r/invoice-credit.json · Import from @/components/ui/invoice-credit

When to use

  • Billing portals and admin tools that show an invoice and let a team credit it
  • Product pages for invoicing software – a document that looks real and can be tried

When not to use

  • Printing or PDF generation – render a dedicated print layout instead
  • Invoices with dozens of lines – the credit tray is built for a handful

Usage

import { InvoiceCredit } from "@/components/ui/invoice-credit";

export function Billing() {
  return (
    <InvoiceCredit
      invoiceNo="INV-2041"
      creditNo="CN-0117"
      lines={[
        { id: "plan", name: "Growth plan", qty: 1, rate: 1250, tax: 0.2 },
        { id: "api", name: "API calls (per 1k)", qty: 18420, rate: 0.42, tax: 0.2, group: "Metered usage" },
      ]}
      onIssue={(credits, total) => api.issueCreditNote(credits, total)}
    />
  );
}

Props

NameTypeDefaultDescription
seller / buyer{ name, lines: string[] }From and To blocks on the invoice
invoiceNo / creditNostringDocument numbers shown under Terms
invoiceDate / dueDate / periodstringDates shown on both documents
linesInvoiceLine[]Each { id, name, qty, rate, tax, group? } – lines with a group are listed under a heading
currencystring"$"Currency symbol
onIssue(credits, total) => voidCalled when the credit note is issued, with the credited amount per line and the total incl. tax
classNamestringExtra class on the root

Keyboard

  • Tab: Moves through the switch, the actions, every line and amount
  • Enter: Confirms an amount

Accessibility

  • The switch is a tablist; the sheet in the back is hidden from screen readers and can't be focused
  • Credit lines are checkboxes with labels; amounts are labelled per line and stay within the line total

Responsive

  • Follows its own width: below 560px the parties stack, the rate and tax columns hide and the credit tray drops the total column
  • Dark mode built in: switches when an ancestor (usually <html>) has the class dark or data-theme="dark"

Requirements

  • React 19