React Invoice Template: Positionen, MwSt. und Druckansicht

Ein typisches React Invoice Template ist ein Formular mit einer Tabelle und einem Steuerfeld unter der Zwischensumme. Das reicht bis zur ersten echten Rechnung: ein Rabatt auf eine Position, ein zweiter auf das ganze Dokument, zwei Steuersätze und ein Kunde, der ein PDF will, das nicht wie ein Screenshot deiner App aussieht. Dieses Tutorial baut in Next.js die Version, die diese Fälle übersteht: einen Rechnungseditor, dessen Positionen sich wie eine Tabellenkalkulation verhalten, Summen mit der MwSt. pro Satz und eine Druckansicht, die der Browser als sauberes A4-PDF speichert. Der Editor ist Invoice Line Items von Wingo UI, der Bibliothek, die wir bauen. Wir sind also nicht neutral; die Summen und das Druckblatt sind Code, den du lesen und kopieren kannst.
Was braucht ein React Invoice Template?
Ein Rechnungsobjekt und zwei Ansichten darauf: einen Editor, der es ändert, und ein Druckblatt, das es nur liest. Egal ob du das Ergebnis React Invoice Generator oder React Invoice Builder nennst, das Objekt enthält die Parteien, die Nummer, die Daten, die Währung, die Positionen und optional einen Rabatt auf das Dokument. Die Summen liegen im Browser nie neben den Positionen im State: Sie werden bei jedem Render aus den Positionen abgeleitet, im Browser für die Anzeige und vor dem Speichern noch einmal auf dem Server.
InvoiceLine ist { id, quantity, unitPrice, vatRate?, discount?, discountType? } plus die gedruckten Felder product, description, unit und vatCategory. Der Satz steht als Dezimalzahl (0.2 für 20 %), und vatCategory unterscheidet die Zeilen mit 0 %. Eine Position zum Nullsatz und eine steuerbefreite Position werden so als verschiedene Zeilen gedruckt.
Wie baue ich den Editor für die Rechnungspositionen?
Installier Invoice Line Items und gib der Komponente deine Produkte, deine Steuersätze und deine Währung. Die Positionen sind der Teil eines Rechnungseditors in React, den man bei jeder Rechnung anfasst. Deshalb steckt dort die meiste Arbeit an der Tastaturbedienung. Die Installation bringt die invoicing-Lib, die Produkt-Combobox und das Währungsfeld mit:
Die Standard-Steuersätze sind die rumänischen (21 %, 11 % und drei Kategorien mit 0 %), weil die Komponente für e-Factura-Rechnungen gebaut wurde. Deine eigene Liste gibst du über vatRates mit. Das Beispiel stellt einem britischen Kunden eine Rechnung in Pfund, mit den drei Sätzen, die GOV.UK aufführt (opens in a new tab), Stand Oktober 2026: 20 % Normalsatz, 5 % ermäßigter Satz und 0 % Nullsatz.
Wählst du ein Produkt, übernimmt die Position seine Einheit, seinen Preis und seinen Steuersatz und behält die Menge. Tippst du einen Namen, der nicht im Katalog steht, kommt er als freier Text dazu, und der Preis bleibt markiert, bis du einen einträgst (allowCustomProducts, standardmäßig an). Bei einem großen Katalog ersetzt du products durch onSearchProducts, eine asynchrone Suche, die die Combobox mit Debounce aufruft.
Baust du die Tabelle für deine Rechnungspositionen mit useFieldArray aus react-hook-form, bekommst du Zeilen zum Hinzufügen und Entfernen und das Produkt aus Menge und Preis. Wie eine Tabellenkalkulation fühlt sie sich erst durch die Tastatur an. Tab geht in Lesereihenfolge durch die Zellen, Escape in einer Zahlenzelle stellt ihren vorherigen Wert wieder her, Enter springt in dieselbe Zelle der nächsten Position, und Cmd oder Strg + Enter fügt von überall eine Position hinzu. Alt + Pfeil nach oben oder unten verschiebt die aktuelle Position, und eine entfernte Position hinterlässt fünf Sekunden lang eine Zeile zum Rückgängigmachen.
$ npx wingo-ui@latest add invoice-line-itemsAuf dem Smartphone zeigt dieselbe Komponente jede Position als Karte. Tippst du eine an, bearbeitest du eine Kopie in einem Bottom Sheet, die du mit Save übernimmst. Der Wechsel richtet sich nach der Breite der Komponente selbst (layout="auto" zeigt die Tabelle ab 768 px Containerbreite). Auch ein schmales Seitenpanel auf dem Desktop bekommt also die Karten. Warum wir den Container messen, erklärt der Guide zu Mobile-first-Komponenten in React.
Wie sollten Rabatte auf einer Rechnung funktionieren?
Zuerst werden die Positionsrabatte abgezogen, dann wird ein Rabatt auf das Dokument vor der Steuer auf die Positionen verteilt. Ein Positionsrabatt, in Prozent oder als Betrag, geht vor allem anderen von dieser Position ab. Ein Rabatt auf das Dokument geht nach den Positionsrabatten von der Zwischensumme ab, und er muss vor der Steuer anteilig nach dem Wert der Positionen auf sie verteilt werden. Ziehst du ihn stattdessen von der Gesamtsumme ab, passt die für jeden Satz gedruckte Steuer nicht mehr zu ihrem steuerpflichtigen Betrag.
computeInvoiceTotals aus der invoicing-Lib erledigt beides und rundet die Steuer einmal pro Satz. Hier die Beispielrechnung, mit 10 % Rabatt auf die Design-Position und 5 % auf das ganze Dokument:
Der Rabatt von 204,50 auf das Dokument teilt sich in 108,00, 31,50 und 65,00 auf die drei Positionen auf. Die letzte Position nimmt, was übrig bleibt, damit die Cent-Beträge aufgehen. Die Zeile mit 20 % wird auf 2.650,50 besteuert, also genau auf den Wert der beiden 20-%-Positionen nach beiden Rabatten.
Warum die Steuer pro Satz statt pro Position berechnen?
Weil sich Rundungsfehler aufsummieren, wenn du jede Position einzeln rundest und dann addierst. Drei Positionen, deren Steuer jeweils auf einen halben Penny endet, runden dreimal auf; dieselbe Basis, einmal besteuert, rundet einmal auf. EN 16931, der europäische Standard für E-Rechnungen, definiert die Steuer jedes Satzes als steuerpflichtigen Betrag dieses Satzes mal Satz, gerundet auf zwei Nachkommastellen (Regel BR-CO-17 (opens in a new tab)), und die Summen des Dokuments müssen sich aus diesen gerundeten Zeilen ergeben. Unser Beitrag zur e-Factura-Steuerberechnung geht die Regel mit einem durchgerechneten Beispiel und den Validierungsfehlern durch, die sie auslöst.
Für das Template heißt das nur eins: Die Steuer pro Position in totals.lines dient zur Information, und jede Summe, die du druckst, speicherst oder verschickst, kommt aus vatBreakdown.
Eine Grenze solltest du kennen, wenn du US-Sales-Tax abrechnest: Die Steuerspalte formatiert Sätze mit höchstens einer Nachkommastelle. Der kombinierte Satz von New York City mit 8⅞ % (Publication 718 (opens in a new tab), gültig ab dem 1. März 2025) würde also als 8,9 % erscheinen. Rechne mit dem exakten Bruch und druck den Satz auf dem Blatt selbst.
Wie baue ich eine saubere Druckansicht für die Rechnung?
Bau ein eigenes Blatt für das Papier und halt den Editor aus dem Ausdruck heraus. Eingabefelder, Ziehgriffe und Hover-Effekte haben auf Papier nichts zu suchen, und ein im Dark Mode gedruckter Editor verschwendet Toner. Das Blatt ist schlichtes HTML mit festen Papierfarben in beiden Themes. Das ist die eine Stelle, an der ein Wingo-UI-Screen fest codierte Farben nutzt: Das Blatt steht für eine gedruckte Seite, also ignoriert es das Theme.
Die Netto-Spalte zeigt jede Position nach beiden Rabatten, also ergeben die Positionen zusammen die steuerpflichtigen Beträge in den Steuerzeilen. Das Blatt hat Rahmenlinien und keine Hintergrundfarben, weil Browser Hintergrundfarben beim Drucken standardmäßig weglassen.
Danach kommen die Seitenregeln in app/globals.css. size in @page ist laut MDN (opens in a new tab) Baseline 2024; für Kunden in Nordamerika nimmst du letter.
Chromium wiederholt ein thead oben auf jeder gedruckten Seite seit einer Änderung vom Juni 2016, die das Verhalten an Firefox und Edge angeglichen hat (der Commit (opens in a new tab)). Die display-Regel macht die Absicht also nur ausdrücklich. Druck eine Testrechnung mit 40 Positionen in jedem Browser, den deine Kunden nutzen, bevor ein Kunde es tut.
Wie drucke ich nur die Rechnung und nicht die App?
Markier alles andere mit print:hidden und render das Blatt mit hidden print:block, solange der Editor auf dem Bildschirm steht. Seitenleiste und Header deines Admin-Layouts brauchen ebenfalls print:hidden, weil sie außerhalb der Seitenkomponente liegen. Damit liefern window.print() und „Als PDF speichern“ im Browser ein PDF, auf dem nur das Dokument steht.
react-to-print (opens in a new tab) macht dasselbe über ein iframe: useReactToPrint({ contentRef }) kopiert den referenzierten Knoten und die Styles der Seite in ein frisches Dokument und druckt dieses, also kannst du dir die print:hidden-Klassen sparen. Zu @react-pdf/renderer greifst du nur, wenn der Server PDFs ganz ohne menschliches Zutun bauen muss, etwa als Anhang geplanter E-Mails. Es rendert eigene Layout-Bausteine, du pflegst das Rechnungslayout dann also doppelt.
Wie sieht der fertige Rechnungseditor aus?
Editor und Blatt teilen sich ein State-Objekt. Die Summen werden nie gespeichert, nur abgeleitet:
In der Vorschau scrollt das 210mm breite Blatt auf dem Smartphone seitlich. Wenn deine Kunden Rechnungen auf dem Smartphone prüfen, miss den Container mit einem ResizeObserver und skalier das Blatt stattdessen mit einer CSS-Transformation. Genau das macht der Block Invoice Page weiter unten.
Wie speichere ich die Rechnung, ohne den Summen des Browsers zu vertrauen?
Schick die Positionen, nie die Summen, und rechne auf dem Server mit derselben Funktion neu. Über die Prop name legt der Editor ein verstecktes Feld mit den Positionen als JSON an, also schickt ein natives Formular sie an eine Server Action:
Die invoicing-Lib importiert nichts aus React. Sie läuft deshalb unverändert in einer Server Action, einem Route Handler oder einem Queue-Worker.
Kann ich stattdessen mit einer kompletten Rechnungsseite starten?
Ja, wenn du aus Rumänien abrechnest. Der Block Invoice Page packt denselben Positionseditor in einen kompletten Screen: eine Kundenauswahl, die nach Name, CUI oder Stadt sucht, Serien und Nummerierung, die vergebene Nummern überspringen, Vorgaben für das Fälligkeitsdatum mit 15, 30 und 45 Tagen, einen Entwurf, der sich 800 ms nach der letzten Änderung selbst speichert (Strg oder Cmd + S speichert sofort), eine A4-Vorschau, die auf dem Smartphone auf den Bildschirm skaliert, einen Dialog zum Versenden und den Schalter für e-Factura. Die Beschriftungen sind auf Englisch, die Felder folgen aber rumänischen B2B-Rechnungen. Für andere Länder ist die Komposition oben deshalb der bessere Startpunkt.
$ npx wingo-ui@latest add invoice-pageFür den Rest des Backoffice drumherum baut das Tutorial zum Next.js-Admin-Dashboard die App-Hülle, die Datentabellen und die Einstellungen, und weitere Screens, Schritt für Schritt gebaut, findest du in der Kategorie Praxis: Screens und Apps.
Nächster Schritt
Installier den Editor mit npx wingo-ui@latest add invoice-line-items, kopier das Blatt und die Druckregeln und druck eine lange Testrechnung in beiden Themes. Invoice Line Items, die invoicing-Lib und Invoice Page gehören zu Wingo UI Pro.
Häufige Fragen
Wie mache ich in React eine druckbare Rechnung?
Render die Rechnung als schlichtes HTML-Blatt mit festen weißen Papierfarben, blende den Editor und die App-Hülle mit print:hidden von Tailwind aus, leg das Papierformat mit @page fest und ruf window.print() auf. Die Option „Als PDF speichern“ des Browsers erzeugt dann ein PDF, auf dem nur das Blatt steht.
Soll ich für Rechnungen react-to-print oder @react-pdf/renderer nehmen?
Nimm den Druck des Browsers, direkt oder über react-to-print, wenn die Rechnung schon als HTML auf dem Bildschirm steht und ein Mensch sie druckt. Nimm @react-pdf/renderer, wenn der Server PDFs selbstständig erzeugen muss, etwa als E-Mail-Anhang, und nimm in Kauf, dass dessen Layout-Bausteine getrennt von deinen Web-Komponenten sind.
Wie ziehe ich einen Rabatt von der ganzen Rechnung ab, wenn die Positionen verschiedene Steuersätze haben?
Verteil den Rabatt vor jeder Steuer anteilig nach dem Wert, den die Positionen nach ihren eigenen Rabatten haben, und gib den Rundungsrest der letzten Position. Dann sinkt der steuerpflichtige Betrag jedes Satzes um seinen Anteil, und die Steuer pro Satz bleibt exakt.
Rechnet man die Steuer auf Rechnungen pro Position oder pro Satz?
Pro Satz, wenn die Rechnung Mehrwertsteuer ausweist: Addier die steuerpflichtigen Beträge aller Positionen mit diesem Satz und runde die Steuer einmal. So definiert EN 16931, der europäische Standard für E-Rechnungen, die Steuer jedes Satzes; die Steuer pro Position kann dann zur Information auf dem Dokument stehen.
Gibt es ein kostenloses React Invoice Template?
Das Druckblatt, das Druck-CSS und die Rundungsregeln aus diesem Tutorial kannst du in jede React-App kopieren. Die Teile von Wingo UI, die es importiert, also Invoice Line Items, die invoicing-Lib mit computeInvoiceTotals und der Block Invoice Page, gehören zum Pro-Plan.
- Rechnungen
- React
- Next.js
- Mehrwertsteuer


