Invoicing TypeScript helper for React
v1.0.0Added in 1.0.0
Invoice math for React billing apps: VAT per rate the way e-Factura checks it, Romanian VAT rates, and CUI, IBAN and CNP validation.
Change the quantities of the invoice lines with − and +: the net per line, the VAT breakdown per rate (21% and 11%), the discounts and the total are one computeInvoiceTotals call. Turn on prices with VAT, set a document discount in percent or as an amount, and see the VAT stay exact per rate. Type any CUI, IBAN or CNP in the checker: it recognises which one it is, validates the control digit, names the bank of a Romanian IBAN and reads the birth date and county from a CNP.
Invoicing is a Pro React helper in the React hooks and utilities category, written in TypeScript for React 19. It installs as source with npx wingo-ui@latest add invoicing, together with Commerce and Number format.
Add to a project
Requirements: React 19, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add invoicingInvoicing is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import computeInvoiceTotals from @/lib/invoicing.
import { computeInvoiceTotals, formatVatRate, isValidCui } from "@/lib/invoicing"
import { formatPrice } from "@/lib/commerce"
const totals = computeInvoiceTotals(
[
{ id: "l1", quantity: 3, unitPrice: 1250, vatRate: 0.21, discount: 10 },
{ id: "l2", quantity: 12, unitPrice: 64.9, vatRate: 0.11 },
],
{ discount: { type: "amount", value: 100 } },
)
totals.vatBreakdown.map((row) => `VAT ${formatVatRate(row.rate, "en-US")}: ${formatPrice(row.vat, { locale: "en-US", currency: "USD" })}`)
formatPrice(totals.total, { locale: "en-US", currency: "USD" }) // "$4,829.10"
isValidCui("RO12345674") // trueAPI
pricesIncludeVatdefaultfalsebooleanoptions.pricesIncludeVat: false for B2B invoices (unit prices without VAT, VAT added), true for shops and B2C quotes (VAT inside the prices).
discountdefault0numberThe demo's document discount, options.discount.value: shared across lines by value after their own discounts, so every VAT rate stays exact.
discountTypedefault"amount""percent" | "amount"options.discount.type: a percent of the subtotal or a fixed amount (dollars in the demo).
localedefault"en-US"stringThe demo's formatting locale (formatPrice from commerce, formatVatRate).
codedefault"RO12345674"stringThe demo's id checker: a CUI (with or without RO), an IBAN or a CNP.
computeInvoiceTotals(lines: InvoiceLineInput[], options?: InvoiceTotalsOptions) => InvoiceTotalsLines are { id, quantity, unitPrice, vatRate? (0.21), discount?, discountType?: "percent" | "amount" }; options { pricesIncludeVat, discount: { type, value }, defaultVatRate, categoryFor, decimals }. Returns lines (gross, discount, net, vat, total each), gross, lineDiscount, documentDiscount, discount, net, vat, total and vatBreakdown (one row per rate: rate, category, net, vat, total, lineCount). VAT is rounded once per rate on its taxable amount (EN 16931 BR-CO-17, what e-Factura validates), so per-line VAT is informative and the totals always add up.
Parts
computeInvoiceLine
functionOne line on its own (gross, discount, net, VAT and total), for the row of a line editor. Takes the same line and options as computeInvoiceTotals.
RO_VAT_RATES
constantOptions for a VAT select, each { value, rate, category, label, description }: 21% standard, 11% reduced, and 0% exempt (E), reverse charge (AE) and intra-community supply (K).
RO_VAT_RATE
constantRomania's standard VAT rate as a fraction, 0.21, in force since Aug 1, 2025. Re-exported from commerce.
RO_REDUCED_VAT_RATE
constantRomania's reduced VAT rate, 0.11, for food, medicines, books and accommodation. Re-exported from commerce.
formatVatRate
functionA VAT rate as text in the locale: formatVatRate(0.21, "en-US") is "21%" ("21 %" in ro-RO).
incrementDocumentNumber
functionThe next number of an invoice series, keeping its padding and prefix: "2026-0412" becomes "2026-0413", "CRP 099" becomes "CRP 100".
parseCui
functionReads a Romanian company code (CUI / CIF) such as "RO 12345674": { digits, vatPayer, valid } with the ANAF control digit checked, or null when it is not 2 to 10 digits.
isValidCui
functionWhether a CUI's control digit holds: RO12345674 is valid, RO12345678 is not.
formatCui
functionA CUI written the standard way: "RO12345674" for VAT payers, "12345674" otherwise. Returns the input unchanged when it is not a CUI.
isValidIban
functionThe ISO 13616 mod-97 check with each country's length, for Romanian and other European IBANs.
formatIban
functionAn IBAN in groups of four for display: "RO49 AAAA 1B31 0075 9384 0000".
getIbanBank
functionThe bank of a Romanian IBAN from its four-letter code, e.g. { code: "BTRL", name: "Banca Transilvania" }; null for other countries or unknown codes.
RO_BANKS
constantRomanian bank names by IBAN bank code (BTRL, RNCB, BRDE, INGB and more), the table getIbanBank reads.
parseCnp
functionReads a Romanian personal numeric code (CNP): { valid, sex, birthDate, countyCode, countyName, anonymous } with the control digit, date and county checked, or null when it is not 13 digits.
isValidCnp
functionWhether a CNP is valid. "0000000000000", the e-Factura placeholder for a buyer who gave no CNP, counts as valid.
VatCategory
typeThe UBL / EN 16931 VAT category codes an e-Factura line carries: S, Z, E, AE, K, G, O.
InvoiceLineInput
type{ id, quantity, unitPrice, vatRate?, discount?, discountType? }, one line of an invoice or quote.
InvoiceTotals
typeWhat computeInvoiceTotals returns: lines, gross, discounts, net, VAT, total and the VAT breakdown per rate.
Examples
VAT rate options
RO_VAT_RATES for a line's VAT select, with the e-Factura category of each.
Invoice numbering
incrementDocumentNumber keeps the prefix and the zero padding of a series.
CUI and IBAN checks
Control digits, the standard written form and the bank of a Romanian IBAN.
Dependencies
Files
- lib/invoicing.ts
FAQ
Is the Invoicing helper free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install Invoicing?
Run npx wingo-ui@latest add invoicing. It also adds the Wingo UI items it builds on (Commerce and Number format). With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/invoicing.
See also
Used in
More React hooks and utilities
Articles using this component
- React Invoice Template: Line Items, VAT and Print View
Build it: screens and apps10 min read
- E-Factura VAT Calculation in TypeScript and React
Build it: screens and apps11 min read
- IBAN Validation JavaScript: Mod 97, Regex and a React Field
Component guides12 min read
- Romanian CNP Validator in TypeScript, Zod and React
Component guides13 min read