Wie man eine PDF-Datei in React erstellt
Willkommen beim Tutorial zur Erstellung von PDF-Dokumenten aus einer React-Anwendung! In diesem Tutorial erkunden wir verschiedene Bibliotheken zur Erstellung von PDFs und lernen, wie Sie die beliebte jsPDF-Bibliothek verwenden, um PDF-Dateien direkt aus Ihren React-Komponenten zu erstellen. Lassen Sie uns eintauchen und loslegen!
PDF (Portable Document Format) ist ein weit verbreitetes Dateiformat zum Teilen und Drucken von Dokumenten, während ihr Layout und ihre Formatierung erhalten bleiben. Als React-Webentwickler könnten Sie auf Szenarien stoßen, in denen Sie PDF-Dokumente erstellen müssen, wie z. B. Rechnungen, Berichte oder Verkaufsverträge, direkt aus Ihrer React-Anwendung.
Auswahl einer React PDF-Bibliothek
PDF-Dokumente in einer React-Anwendung zu erstellen, kann eine herausfordernde Aufgabe sein, besonders wenn Sie neu in dieser Umgebung sind. Glücklicherweise stehen uns mehrere Drittanbieter-Bibliotheken zur Verfügung, die diesen Prozess erheblich vereinfachen. Jede Bibliothek hat ihre eigenen einzigartigen Merkmale und Werkzeuge, die auf unterschiedliche Anwendungsfälle abzielen. Lassen Sie uns diese Bibliotheken etwas genauer betrachten.
jsPDF
jsPDF ist eine weit verbreitete Bibliothek unter Entwicklern zur Erstellung von PDF-Dateien aus JavaScript. Einer der Hauptvorteile ist seine Einfachheit. Seine Syntax und Verwendung sind recht unkompliziert, sodass Sie Ihre HTML-Inhalte im Handumdrehen in eine PDF-Datei umwandeln können.
Sie ermöglicht es Ihnen, das Format und Layout Ihrer PDFs zu steuern, von der Änderung der Schriftgröße und -farbe bis hin zur Anpassung der Seitenorientierung und -größe. jsPDF ist eine robuste Lösung, die sowohl in Browser- als auch in Serverumgebungen funktioniert, was sie zu einer hervorragenden Wahl für eine Vielzahl von JavaScript-Anwendungen macht.
pdfmake
pdfmake sticht als client-/serverseitige PDF-Drucklösung in reinem JavaScript hervor. Diese Bibliothek ist eine ausgezeichnete Wahl für die Erstellung komplexerer PDFs, dank ihrer umfassenden API und flexiblen Layout-Optionen. Mit pdfmake können Sie den Inhalt und die Struktur Ihres Dokuments mit einem einfachen JavaScript-Objekt definieren und dann in ein gültiges PDF-Dokument umwandeln.
React-PDF
React-PDF ist eine einzigartige Bibliothek, die leistungsstarke Funktionen zur Erstellung von PDF-Dateien mithilfe von React-Komponenten bietet. Anstatt Ihre Dokumentstruktur manuell in einem JavaScript-Objekt zu schreiben, können Sie Ihr PDF erstellen, wie Sie eine typische React-Anwendung entwickeln - mit wiederverwendbaren Komponenten und Props. Die IronPDF-Website bietet ein Tutorial zur Erstellung von PDFs mit der React-PDF-Bibliothek.
Warum jsPDF wählen?
Während alle drei Bibliotheken leistungsstarke Werkzeuge zur Erstellung von PDF-Dokumenten in React bereitstellen, verwenden wir in diesem Tutorial jsPDF aufgrund der Einfachheit, Flexibilität und weit verbreiteten Akzeptanz in der Community. Es bietet einen niedrigeren Einstieg für Anfänger, und sein robustes Funktionsset macht es zu einer geeigneten Wahl für viele Anwendungsfälle. Die Prinzipien, die wir mit jsPDF erkunden, geben Ihnen eine solide Grundlage zur Erstellung von PDFs, und Sie werden in der Lage sein, andere Bibliotheken leichter zu erlernen, wenn Ihr Projekt es erfordert.
Voraussetzungen
Bevor wir in dieses Tutorial eintauchen, ist es wichtig, sicherzustellen, dass Sie mit den notwendigen Werkzeugen und dem Wissen ausgestattet sind, um reibungslos mitzukommen. Die Voraussetzungen für dieses Tutorial sind wie folgt:
Grundlegendes Verständnis von React
Zuallererst sollten Sie ein grundlegendes Verständnis von React haben, einer beliebten JavaScript-Bibliothek zum Erstellen von Benutzeroberflächen, insbesondere von Single-Page-Anwendungen. Sie sollten mit Konzepten wie JSX (JavaScript XML), Komponenten, Zustand und Props in React vertraut sein.
Entwicklungsumgebung
Sie sollten auch eine Entwicklungsumgebung auf Ihrem Computer eingerichtet haben, um React-Anwendungen zu erstellen. Dazu gehört ein Texteditor oder eine integrierte Entwicklungsumgebung (IDE). Texteditoren wie Visual Studio Code, Atom oder Sublime Text sind alles gute Optionen.
Node.js und npm
Zur Verwaltung unseres Projekts und seiner Abhängigkeiten verwenden wir Node.js und npm (Node Package Manager). Stellen Sie sicher, dass Sie Node.js auf Ihrem Computer installiert haben. Node.js ist eine JavaScript-Laufzeitumgebung, die es uns ermöglicht, JavaScript auf unseren Servern auszuführen. Es enthält npm, sodass Sie die für Ihr Projekt benötigten Bibliotheken verwalten können.
Sie können überprüfen, ob Node.js und npm installiert sind, indem Sie die folgenden Terminalbefehle ausführen:
node -v
npm -v
node -v
npm -v
Diese Befehle zeigen die auf Ihrem System installierte Version von Node.js bzw. npm an. Wenn Sie sie nicht installiert haben oder Ihre Versionen veraltet sind, sollten Sie die neueste Long Term Support (LTS)-Version von Node.js von ihrer offiziellen Download-Seite herunterladen und installieren.
Schritt 1: Einrichten des Projekts
Beginnen wir mit der Einrichtung unseres React-Projekts. Öffnen Sie Ihr Terminal und navigieren Sie zu dem gewünschten Verzeichnis, in dem Sie Ihr Projekt erstellen möchten. Führen Sie den folgenden Befehl aus, um eine neue React-Anwendung zu erstellen:
npx create-react-app pdf-from-react
npx create-react-app pdf-from-react

Dieser Befehl erstellt ein neues Verzeichnis namens pdf-from-react mit einer grundlegenden React-Projektstruktur.
Wechseln Sie als nächstes in das Verzeichnis des Projekts:
cd pdf-from-react
cd pdf-from-react
Nun können wir das Projekt in unserem Code-Editor öffnen und mit der Implementierung fortfahren.
Schritt 2: Hinzufügen der erforderlichen Abhängigkeiten
Zuerst müssen wir die erforderlichen Pakete installieren. Installieren Sie react, react-dom, @mui/material und jspdf mit dem folgenden Terminalbefehl.
npm install jspdf @mui/material @emotion/react @emotion/styled @mui/icons-material
npm install jspdf @mui/material @emotion/react @emotion/styled @mui/icons-material
Schritt 3: Gestaltung der PDF-Erstellungsfunktion
Importieren von Bibliotheken
Wir beginnen damit, die notwendigen Abhängigkeiten für unsere Anwendung zu importieren. Dazu gehören verschiedene Komponenten aus der Material-UI-Bibliothek, die jsPDF-Bibliothek zur Erstellung von PDFs und Styling-Hilfsmittel.
import React, { useState } from "react";
import "./App.css";
import {
Button,
TextField,
Box,
Container,
Typography,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
IconButton,
Snackbar,
Alert,
} from "@mui/material";
import Grid from "@mui/material/Grid";
import DeleteIcon from "@mui/icons-material/Delete";
import jsPDF from "jspdf";
import { styled } from "@mui/material/styles";
import { tableCellClasses } from "@mui/material/TableCell";
import React, { useState } from "react";
import "./App.css";
import {
Button,
TextField,
Box,
Container,
Typography,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
IconButton,
Snackbar,
Alert,
} from "@mui/material";
import Grid from "@mui/material/Grid";
import DeleteIcon from "@mui/icons-material/Delete";
import jsPDF from "jspdf";
import { styled } from "@mui/material/styles";
import { tableCellClasses } from "@mui/material/TableCell";
Erstellen von gestylten Komponenten
Um ein konsistentes Verhalten über verschiedene Browser hinweg in unserer App hinzuzufügen, haben wir das styled-Utility aus der MUI-Bibliothek verwendet, um StyledTableCell und StyledTableRow zu erstellen.
const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white,
},
[`&.${tableCellClasses.body}`]: {
fontSize: 14,
},
}));
const StyledTableRow = styled(TableRow)(({ theme }) => ({
"&:nth-of-type(odd)": {
backgroundColor: theme.palette.action.hover,
},
"&:last-child td, &:last-child th": {
border: 0,
},
}));
const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white,
},
[`&.${tableCellClasses.body}`]: {
fontSize: 14,
},
}));
const StyledTableRow = styled(TableRow)(({ theme }) => ({
"&:nth-of-type(odd)": {
backgroundColor: theme.palette.action.hover,
},
"&:last-child td, &:last-child th": {
border: 0,
},
}));
Erstellen der App-Komponente
Die Hauptkomponente unserer Anwendung ist die App-Komponente. Wir haben vier Zustandsvariablen: customerName und customerAddress zur Verfolgung der Kundendaten, items zur Verfolgung der Listenelemente in der Rechnung und error zur Anzeige einer Fehlermeldung bei Bedarf.
function App() {
// State variables
const [customerName, setCustomerName] = useState("");
const [customerAddress, setCustomerAddress] = useState("");
const [items, setItems] = useState([{ name: "", quantity: "", price: "" }]);
const [error, setError] = useState(false);
function App() {
// State variables
const [customerName, setCustomerName] = useState("");
const [customerAddress, setCustomerAddress] = useState("");
const [items, setItems] = useState([{ name: "", quantity: "", price: "" }]);
const [error, setError] = useState(false);
Verarbeiten von Benutzereingaben
In diesem Codeblock haben wir Funktionen definiert, um Benutzerinteraktionen zu verarbeiten: Ändern von Artikeldetails, Hinzufügen eines neuen Artikels und Löschen eines Artikels. Die handleItemChange-Funktion aktualisiert die Eigenschaften eines Elements, wenn ein Benutzer diese ändert. Die addItem-Funktion fügt ein neues Element zur Liste hinzu. Die deleteItem-Funktion entfernt ein Element aus der Liste.
const handleItemChange = (index, event) => {
let newItems = [...items];
newItems[index][event.target.name] = event.target.value;
setItems(newItems);
};
const addItem = () => {
setItems([...items, { name: "", quantity: "", price: "" }]);
};
const deleteItem = (index) => {
let newItems = [...items];
newItems.splice(index, 1);
setItems(newItems);
};
const handleItemChange = (index, event) => {
let newItems = [...items];
newItems[index][event.target.name] = event.target.value;
setItems(newItems);
};
const addItem = () => {
setItems([...items, { name: "", quantity: "", price: "" }]);
};
const deleteItem = (index) => {
let newItems = [...items];
newItems.splice(index, 1);
setItems(newItems);
};
Erstellen der Rechnung
Folgendes ist der generateInvoice-Funktionscode:
// Generate invoice
const generateInvoice = () => {
// Validate the input fields
if (
!customerName ||
!customerAddress ||
items.some((item) => !item.name || !item.quantity || !item.price)
) {
setError(true);
return;
}
// Create a new jsPDF instance
let doc = new jsPDF("p", "pt");
// Add invoice header
doc.setFontSize(24);
doc.text("Invoice", 40, 60);
doc.setFontSize(10);
doc.text("Invoice Number: 123456", 40, 90);
doc.text("Date: " + new Date().toDateString(), 40, 110);
doc.text(`Customer Name: ${customerName}`, 40, 130);
doc.text(`Customer Address: ${customerAddress}`, 40, 150);
// Add items section
doc.setFontSize(14);
doc.text("Items:", 40, 200);
doc.line(40, 210, 550, 210);
// Add item details
doc.setFontSize(12);
let yOffset = 240;
let total = 0;
items.forEach((item) => {
let itemTotal = item.quantity * item.price;
total += itemTotal;
doc.text(`Item: ${item.name}`, 40, yOffset);
doc.text(`Quantity: ${item.quantity}`, 200, yOffset);
doc.text(`Price: $${item.price}`, 300, yOffset);
doc.text(`Total: $${itemTotal}`, 400, yOffset);
yOffset += 20;
});
// Add total
doc.line(40, yOffset, 550, yOffset);
doc.setFontSize(14);
doc.text(`Total: $${total}`, 400, yOffset + 30);
// Save the generated PDF as "invoice.pdf"
doc.save("invoice.pdf");
// Reset error state
setError(false);
};
// Generate invoice
const generateInvoice = () => {
// Validate the input fields
if (
!customerName ||
!customerAddress ||
items.some((item) => !item.name || !item.quantity || !item.price)
) {
setError(true);
return;
}
// Create a new jsPDF instance
let doc = new jsPDF("p", "pt");
// Add invoice header
doc.setFontSize(24);
doc.text("Invoice", 40, 60);
doc.setFontSize(10);
doc.text("Invoice Number: 123456", 40, 90);
doc.text("Date: " + new Date().toDateString(), 40, 110);
doc.text(`Customer Name: ${customerName}`, 40, 130);
doc.text(`Customer Address: ${customerAddress}`, 40, 150);
// Add items section
doc.setFontSize(14);
doc.text("Items:", 40, 200);
doc.line(40, 210, 550, 210);
// Add item details
doc.setFontSize(12);
let yOffset = 240;
let total = 0;
items.forEach((item) => {
let itemTotal = item.quantity * item.price;
total += itemTotal;
doc.text(`Item: ${item.name}`, 40, yOffset);
doc.text(`Quantity: ${item.quantity}`, 200, yOffset);
doc.text(`Price: $${item.price}`, 300, yOffset);
doc.text(`Total: $${itemTotal}`, 400, yOffset);
yOffset += 20;
});
// Add total
doc.line(40, yOffset, 550, yOffset);
doc.setFontSize(14);
doc.text(`Total: $${total}`, 400, yOffset + 30);
// Save the generated PDF as "invoice.pdf"
doc.save("invoice.pdf");
// Reset error state
setError(false);
};
In der generateInvoice-Funktion führen wir zunächst eine Validierung der Eingabefelder durch, um sicherzustellen, dass der Kundenname, die Kundenadresse und die Artikeldetails ausgefüllt sind. Wenn eines dieser Felder leer ist, setzen wir den error-Zustand auf true und geben sofort zurück.
Anschließend erstellen wir eine neue Instanz von jsPDF, indem wir new jsPDF("p", "pt") aufrufen. Das erste Argument "p" spezifiziert die Seitenorientierung als Hochformat, und das zweite Argument "pt"" spezifiziert die Maßeinheit als Punkte.
Wir beginnen dann mit dem Hinzufügen von Inhalten zu unserem PDF-Dokument. Wir setzen die Schriftgröße mit doc.setFontSize und verwenden die doc.text-Methode, um Text an bestimmten Koordinaten auf der Seite hinzuzufügen. Wir fügen den Rechnungsheader hinzu, einschließlich Titel, Rechnungsnummer, Datum, Kundenname und Kundenadresse.
Nach dem Header fügen wir den Abschnitt "Items" hinzu, indem wir die Schriftgröße setzen und mit doc.line eine Linie ziehen, um die Artikel vom Header zu trennen. Anschließend iterieren wir über jedes Element im items-Array und berechnen den Gesamtpreis für jedes Element, indem wir die Menge mit dem Preis multiplizieren. Wir aktualisieren die total-Variable mit der Summe aller Artikelpreise.
Für jedes Element verwenden wir doc.text, um den Artikelnamen, die Menge, den Preis und den Artikelgesamtpreis im PDF-Dokument zu ergänzen. Wir erhöhen die yOffset-Variable, um zur nächsten Zeile für jedes Element zu wechseln. Schließlich fügen wir eine Linie hinzu, um die Artikel vom Gesamtbetrag zu trennen, und verwenden doc.text, um den Gesamtbetrag unten rechts im Dokument hinzuzufügen.
Sobald der Inhalt hinzugefügt ist, verwenden wir doc.save("invoice.pdf"), um das erstellte PDF als "invoice.pdf" auf dem Computer des Benutzers zu speichern. Schließlich setzen wir den error-Zustand auf false zurück, um vorherige Validierungsfehler zu löschen.
Schritt 4: UI-Rendering
Die return-Anweisung enthält den JSX-Code, der den Renderprozess steuert. Es enthält Eingabefelder für den Kundennamen und die Adresse, eine Tabelle zum Eingeben von Artikeldetails, Schaltflächen zum Hinzufügen von Artikeln und zum Erstellen der Rechnung sowie ein Fehler-Snackbar zum Anzeigen von Validierungsfehlern.
Es verwendet Komponenten aus der Material-UI-Bibliothek, wie Button, TextField, Box, Container, Typography, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, IconButton, Snackbar und Alert, um grundlegende Komponenten zu erstellen. Diese Komponenten werden verwendet, um die Formularelemente, Tabellen, Schaltflächen und Fehlermeldungen zu erstellen.
return (
<Container maxWidth="md">
<Box sx={{my: 4}}>
<Typography variant="h3" component="h1" gutterBottom>
Create Invoice
</Typography>
{/* Customer Name and Address fields */}
<Grid container spacing={3}>
<Grid item xs={6}>
<TextField
label="Customer Name"
fullWidth
margin="normal"
value={customerName}
onChange={(e) => setCustomerName(e.target.value)}
/>
</Grid>
<Grid item xs={6}>
<TextField
label="Customer Address"
fullWidth
margin="normal"
value={customerAddress}
onChange={(e) => setCustomerAddress(e.target.value)}
/>
</Grid>
</Grid>
{/* Items table */}
<TableContainer component={Paper}>
<Table sx={{minWidth: 700}} aria-label="invoice table">
<TableHead>
<TableRow>
<StyledTableCell>Item Name</StyledTableCell>
<StyledTableCell align="left">Quantity</StyledTableCell>
<StyledTableCell align="left">Price</StyledTableCell>
<StyledTableCell align="left">Action</StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{items.map((item, index) => (
<StyledTableRow key={index}>
<StyledTableCell component="th" scope="row">
<TextField
fullWidth
value={item.name}
onChange={(event) => handleItemChange(index, event)}
name="name"
/>
</StyledTableCell>
<StyledTableCell align="right">
<TextField
fullWidth
value={item.quantity}
onChange={(event) => handleItemChange(index, event)}
name="quantity"
/>
</StyledTableCell>
<StyledTableCell align="right">
<TextField
fullWidth
value={item.price}
onChange={(event) => handleItemChange(index, event)}
name="price"
/>
</StyledTableCell>
<StyledTableCell align="right">
<IconButton onClick={() => deleteItem(index)}>
<DeleteIcon />
</IconButton>
</StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>
{/* Buttons */}
<Box mt={2} display="flex" gap={2}>
<Button variant="contained" onClick={addItem}>
Add Item
</Button>
<Button variant="outlined" color="success" onClick={generateInvoice}>
Generate Invoice
</Button>
</Box>
</Box>
{/* Error Snackbar */}
<Snackbar
open={error}
autoHideDuration={6000}
onClose={() => setError(false)}
anchorOrigin={{vertical: "top", horizontal: "right"}}
>
<Alert onClose={() => setError(false)} severity="error">
Please fill in all required fields.
</Alert>
</Snackbar>
</Container>
);
return (
<Container maxWidth="md">
<Box sx={{my: 4}}>
<Typography variant="h3" component="h1" gutterBottom>
Create Invoice
</Typography>
{/* Customer Name and Address fields */}
<Grid container spacing={3}>
<Grid item xs={6}>
<TextField
label="Customer Name"
fullWidth
margin="normal"
value={customerName}
onChange={(e) => setCustomerName(e.target.value)}
/>
</Grid>
<Grid item xs={6}>
<TextField
label="Customer Address"
fullWidth
margin="normal"
value={customerAddress}
onChange={(e) => setCustomerAddress(e.target.value)}
/>
</Grid>
</Grid>
{/* Items table */}
<TableContainer component={Paper}>
<Table sx={{minWidth: 700}} aria-label="invoice table">
<TableHead>
<TableRow>
<StyledTableCell>Item Name</StyledTableCell>
<StyledTableCell align="left">Quantity</StyledTableCell>
<StyledTableCell align="left">Price</StyledTableCell>
<StyledTableCell align="left">Action</StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{items.map((item, index) => (
<StyledTableRow key={index}>
<StyledTableCell component="th" scope="row">
<TextField
fullWidth
value={item.name}
onChange={(event) => handleItemChange(index, event)}
name="name"
/>
</StyledTableCell>
<StyledTableCell align="right">
<TextField
fullWidth
value={item.quantity}
onChange={(event) => handleItemChange(index, event)}
name="quantity"
/>
</StyledTableCell>
<StyledTableCell align="right">
<TextField
fullWidth
value={item.price}
onChange={(event) => handleItemChange(index, event)}
name="price"
/>
</StyledTableCell>
<StyledTableCell align="right">
<IconButton onClick={() => deleteItem(index)}>
<DeleteIcon />
</IconButton>
</StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>
{/* Buttons */}
<Box mt={2} display="flex" gap={2}>
<Button variant="contained" onClick={addItem}>
Add Item
</Button>
<Button variant="outlined" color="success" onClick={generateInvoice}>
Generate Invoice
</Button>
</Box>
</Box>
{/* Error Snackbar */}
<Snackbar
open={error}
autoHideDuration={6000}
onClose={() => setError(false)}
anchorOrigin={{vertical: "top", horizontal: "right"}}
>
<Alert onClose={() => setError(false)} severity="error">
Please fill in all required fields.
</Alert>
</Snackbar>
</Container>
);
Vollständiger App.js- und App.css-Code
Hier ist der vollständige App.js-Code, den Sie in Ihr Projekt kopieren und einfügen können:
import React, { useState } from "react";
import "./App.css";
import {
Button,
TextField,
Box,
Container,
Typography,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
IconButton,
Snackbar,
Alert,
} from "@mui/material";
import Grid from "@mui/material/Grid";
import DeleteIcon from "@mui/icons-material/Delete";
import jsPDF from "jspdf";
import { styled } from "@mui/material/styles";
import { tableCellClasses } from "@mui/material/TableCell";
const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white,
},
[`&.${tableCellClasses.body}`]: {
fontSize: 14,
},
}));
const StyledTableRow = styled(TableRow)(({ theme }) => ({
"&:nth-of-type(odd)": {
backgroundColor: theme.palette.action.hover,
},
"&:last-child td, &:last-child th": {
border: 0,
},
}));
function App() {
// State variables
const [customerName, setCustomerName] = useState("");
const [customerAddress, setCustomerAddress] = useState("");
const [items, setItems] = useState([{ name: "", quantity: "", price: "" }]);
const [error, setError] = useState(false);
// Event handler for item changes
const handleItemChange = (index, event) => {
let newItems = [...items];
newItems[index][event.target.name] = event.target.value;
setItems(newItems);
};
// Add new item to the list
const addItem = () => {
setItems([...items, { name: "", quantity: "", price: "" }]);
};
// Delete an item from the list
const deleteItem = (index) => {
let newItems = [...items];
newItems.splice(index, 1);
setItems(newItems);
};
// Generate invoice
const generateInvoice = () => {
// Validate the input fields
if (
!customerName ||
!customerAddress ||
items.some((item) => !item.name || !item.quantity || !item.price)
) {
setError(true);
return;
}
// Create a new jsPDF instance
let doc = new jsPDF("p", "pt");
// Add invoice header
doc.setFontSize(24);
doc.text("Invoice", 40, 60);
doc.setFontSize(10);
doc.text("Invoice Number: 123456", 40, 90);
doc.text("Date: " + new Date().toDateString(), 40, 110);
doc.text(`Customer Name: ${customerName}`, 40, 130);
doc.text(`Customer Address: ${customerAddress}`, 40, 150);
// Add items section
doc.setFontSize(14);
doc.text("Items:", 40, 200);
doc.line(40, 210, 550, 210);
// Add item details
doc.setFontSize(12);
let yOffset = 240;
let total = 0;
items.forEach((item) => {
let itemTotal = item.quantity * item.price;
total += itemTotal;
doc.text(`Item: ${item.name}`, 40, yOffset);
doc.text(`Quantity: ${item.quantity}`, 200, yOffset);
doc.text(`Price: $${item.price}`, 300, yOffset);
doc.text(`Total: $${itemTotal}`, 400, yOffset);
yOffset += 20;
});
// Add total
doc.line(40, yOffset, 550, yOffset);
doc.setFontSize(14);
doc.text(`Total: $${total}`, 400, yOffset + 30);
// Save the generated PDF as "invoice.pdf"
doc.save("invoice.pdf");
// Reset error state
setError(false);
};
return (
<Container maxWidth="md">
<Box sx={{my: 4}}>
<Typography variant="h3" component="h1" gutterBottom>
Create Invoice
</Typography>
{/* Customer Name and Address fields */}
<Grid container spacing={3}>
<Grid item xs={6}>
<TextField
label="Customer Name"
fullWidth
margin="normal"
value={customerName}
onChange={(e) => setCustomerName(e.target.value)}
/>
</Grid>
<Grid item xs={6}>
<TextField
label="Customer Address"
fullWidth
margin="normal"
value={customerAddress}
onChange={(e) => setCustomerAddress(e.target.value)}
/>
</Grid>
</Grid>
{/* Items table */}
<TableContainer component={Paper}>
<Table sx={{minWidth: 700}} aria-label="invoice table">
<TableHead>
<TableRow>
<StyledTableCell>Item Name</StyledTableCell>
<StyledTableCell align="left">Quantity</StyledTableCell>
<StyledTableCell align="left">Price</StyledTableCell>
<StyledTableCell align="left">Action</StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{items.map((item, index) => (
<StyledTableRow key={index}>
<StyledTableCell component="th" scope="row">
<TextField
fullWidth
value={item.name}
onChange={(event) => handleItemChange(index, event)}
name="name"
/>
</StyledTableCell>
<StyledTableCell align="right">
<TextField
fullWidth
value={item.quantity}
onChange={(event) => handleItemChange(index, event)}
name="quantity"
/>
</StyledTableCell>
<StyledTableCell align="right">
<TextField
fullWidth
value={item.price}
onChange={(event) => handleItemChange(index, event)}
name="price"
/>
</StyledTableCell>
<StyledTableCell align="right">
<IconButton onClick={() => deleteItem(index)}>
<DeleteIcon />
</IconButton>
</StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>
{/* Buttons */}
<Box mt={2} display="flex" gap={2}>
<Button variant="contained" onClick={addItem}>
Add Item
</Button>
<Button variant="outlined" color="success" onClick={generateInvoice}>
Generate Invoice
</Button>
</Box>
</Box>
{/* Error Snackbar */}
<Snackbar
open={error}
autoHideDuration={6000}
onClose={() => setError(false)}
anchorOrigin={{vertical: "top", horizontal: "right"}}
>
<Alert onClose={() => setError(false)} severity="error">
Please fill in all required fields.
</Alert>
</Snackbar>
</Container>
);
}
export default App;
import React, { useState } from "react";
import "./App.css";
import {
Button,
TextField,
Box,
Container,
Typography,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Paper,
IconButton,
Snackbar,
Alert,
} from "@mui/material";
import Grid from "@mui/material/Grid";
import DeleteIcon from "@mui/icons-material/Delete";
import jsPDF from "jspdf";
import { styled } from "@mui/material/styles";
import { tableCellClasses } from "@mui/material/TableCell";
const StyledTableCell = styled(TableCell)(({ theme }) => ({
[`&.${tableCellClasses.head}`]: {
backgroundColor: theme.palette.common.black,
color: theme.palette.common.white,
},
[`&.${tableCellClasses.body}`]: {
fontSize: 14,
},
}));
const StyledTableRow = styled(TableRow)(({ theme }) => ({
"&:nth-of-type(odd)": {
backgroundColor: theme.palette.action.hover,
},
"&:last-child td, &:last-child th": {
border: 0,
},
}));
function App() {
// State variables
const [customerName, setCustomerName] = useState("");
const [customerAddress, setCustomerAddress] = useState("");
const [items, setItems] = useState([{ name: "", quantity: "", price: "" }]);
const [error, setError] = useState(false);
// Event handler for item changes
const handleItemChange = (index, event) => {
let newItems = [...items];
newItems[index][event.target.name] = event.target.value;
setItems(newItems);
};
// Add new item to the list
const addItem = () => {
setItems([...items, { name: "", quantity: "", price: "" }]);
};
// Delete an item from the list
const deleteItem = (index) => {
let newItems = [...items];
newItems.splice(index, 1);
setItems(newItems);
};
// Generate invoice
const generateInvoice = () => {
// Validate the input fields
if (
!customerName ||
!customerAddress ||
items.some((item) => !item.name || !item.quantity || !item.price)
) {
setError(true);
return;
}
// Create a new jsPDF instance
let doc = new jsPDF("p", "pt");
// Add invoice header
doc.setFontSize(24);
doc.text("Invoice", 40, 60);
doc.setFontSize(10);
doc.text("Invoice Number: 123456", 40, 90);
doc.text("Date: " + new Date().toDateString(), 40, 110);
doc.text(`Customer Name: ${customerName}`, 40, 130);
doc.text(`Customer Address: ${customerAddress}`, 40, 150);
// Add items section
doc.setFontSize(14);
doc.text("Items:", 40, 200);
doc.line(40, 210, 550, 210);
// Add item details
doc.setFontSize(12);
let yOffset = 240;
let total = 0;
items.forEach((item) => {
let itemTotal = item.quantity * item.price;
total += itemTotal;
doc.text(`Item: ${item.name}`, 40, yOffset);
doc.text(`Quantity: ${item.quantity}`, 200, yOffset);
doc.text(`Price: $${item.price}`, 300, yOffset);
doc.text(`Total: $${itemTotal}`, 400, yOffset);
yOffset += 20;
});
// Add total
doc.line(40, yOffset, 550, yOffset);
doc.setFontSize(14);
doc.text(`Total: $${total}`, 400, yOffset + 30);
// Save the generated PDF as "invoice.pdf"
doc.save("invoice.pdf");
// Reset error state
setError(false);
};
return (
<Container maxWidth="md">
<Box sx={{my: 4}}>
<Typography variant="h3" component="h1" gutterBottom>
Create Invoice
</Typography>
{/* Customer Name and Address fields */}
<Grid container spacing={3}>
<Grid item xs={6}>
<TextField
label="Customer Name"
fullWidth
margin="normal"
value={customerName}
onChange={(e) => setCustomerName(e.target.value)}
/>
</Grid>
<Grid item xs={6}>
<TextField
label="Customer Address"
fullWidth
margin="normal"
value={customerAddress}
onChange={(e) => setCustomerAddress(e.target.value)}
/>
</Grid>
</Grid>
{/* Items table */}
<TableContainer component={Paper}>
<Table sx={{minWidth: 700}} aria-label="invoice table">
<TableHead>
<TableRow>
<StyledTableCell>Item Name</StyledTableCell>
<StyledTableCell align="left">Quantity</StyledTableCell>
<StyledTableCell align="left">Price</StyledTableCell>
<StyledTableCell align="left">Action</StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
{items.map((item, index) => (
<StyledTableRow key={index}>
<StyledTableCell component="th" scope="row">
<TextField
fullWidth
value={item.name}
onChange={(event) => handleItemChange(index, event)}
name="name"
/>
</StyledTableCell>
<StyledTableCell align="right">
<TextField
fullWidth
value={item.quantity}
onChange={(event) => handleItemChange(index, event)}
name="quantity"
/>
</StyledTableCell>
<StyledTableCell align="right">
<TextField
fullWidth
value={item.price}
onChange={(event) => handleItemChange(index, event)}
name="price"
/>
</StyledTableCell>
<StyledTableCell align="right">
<IconButton onClick={() => deleteItem(index)}>
<DeleteIcon />
</IconButton>
</StyledTableCell>
</StyledTableRow>
))}
</TableBody>
</Table>
</TableContainer>
{/* Buttons */}
<Box mt={2} display="flex" gap={2}>
<Button variant="contained" onClick={addItem}>
Add Item
</Button>
<Button variant="outlined" color="success" onClick={generateInvoice}>
Generate Invoice
</Button>
</Box>
</Box>
{/* Error Snackbar */}
<Snackbar
open={error}
autoHideDuration={6000}
onClose={() => setError(false)}
anchorOrigin={{vertical: "top", horizontal: "right"}}
>
<Alert onClose={() => setError(false)} severity="error">
Please fill in all required fields.
</Alert>
</Snackbar>
</Container>
);
}
export default App;
Hier ist der App.css-Code:
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;700&display=swap');
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: bold;
/* This is the weight for bold in Poppins */
color: #FF6347;
/* This is the color Tomato. Replace with your preferred color */
}
body {
font-family: 'Poppins', sans-serif;
background-color: #E9F8F4;
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
Schritt 5: Testen der Anwendung
Um die PDF-Erstellungsfunktion zu testen, führen Sie den folgenden Befehl im Terminal aus:
npm start
npm start
Dies wird den Entwicklungsserver starten, und Sie können die Anwendung in Ihrem Browser unter http://localhost:3000 anzeigen.

Füllen Sie die Kundenname, Adresse und Artikeldetails in die Eingabefelder ein und klicken Sie auf die Schaltfläche "Rechnung erstellen". Die PDF-Datei wird auf Ihren Computer heruntergeladen, und Sie können sie öffnen, um die Vollbildansicht der erzeugten Rechnung zu sehen.

Wenn Sie auf die Schaltfläche "Rechnung erstellen" klicken, wird die PDF-Datei erstellt.

Wenn Sie versuchen, ein PDF zu erstellen, obwohl ein Feld leer ist, wird oben rechts eine Fehlermeldung angezeigt.

IronPDF - Die Node.js PDF Bibliothek
IronPDF for Node.js ist eine umfassende Node.js-PDF-Bibliothek, die sich durch Genauigkeit, Benutzerfreundlichkeit und Geschwindigkeit auszeichnet. Es bietet eine Vielzahl von Funktionen zum Erstellen, Bearbeiten und Formatieren von PDFs direkt aus HTML, URLs und Bildern in React. Mit Unterstützung für verschiedene Plattformen, einschließlich Windows, MacOS, Linux, Docker und Cloud-Plattformen wie Azure und AWS, sorgt IronPDF für plattformübergreifende Kompatibilität. Sein benutzerfreundliches API ermöglicht es Entwicklern, die PDF-Erzeugung und -Manipulation schnell in ihre Node.js-Projekte zu integrieren.
Zu den bemerkenswerten Funktionen von IronPDF Node.js gehören: pixelgenaue Wiedergabe, umfangreiche Formatierungsoptionen und erweiterte Bearbeitungsmöglichkeiten wie das Zusammenführen und Aufteilen von PDFs, das Hinzufügen von Anmerkungen und das Erstellen von PDF-Formularen.
Hier ist ein Beispiel zur Erstellung eines PDF-Dokuments aus einer HTML-Datei, einem HTML-String und einer URL:
import { PdfDocument } from "@ironsoftware/ironpdf";
(async () => {
const pdfFromUrl = await PdfDocument.fromUrl("https://getbootstrap.com/");
await pdfFromUrl.saveAs("website.pdf");
const pdfFromHtmlFile = await PdfDocument.fromHtml("design.html");
await pdfFromHtmlFile.saveAs("markup.pdf");
const pdfFromHtmlString = await PdfDocument.fromHtml("<p>Hello World</p>");
await pdfFromHtmlString.saveAs("markup_with_assets.pdf");
})();
import { PdfDocument } from "@ironsoftware/ironpdf";
(async () => {
const pdfFromUrl = await PdfDocument.fromUrl("https://getbootstrap.com/");
await pdfFromUrl.saveAs("website.pdf");
const pdfFromHtmlFile = await PdfDocument.fromHtml("design.html");
await pdfFromHtmlFile.saveAs("markup.pdf");
const pdfFromHtmlString = await PdfDocument.fromHtml("<p>Hello World</p>");
await pdfFromHtmlString.saveAs("markup_with_assets.pdf");
})();
Für weitere Codebeispiele für PDF-bezogene Aufgaben besuchen Sie bitte diese IronPDF-Codebeispiele-Seite.
Abschluss
Abschließend muss das Erstellen von PDFs in einer React-Anwendung nicht einschüchternd sein. Mit den richtigen Werkzeugen und einem klaren Verständnis können Sie mühelos schöne, gut strukturierte PDF-Dokumente erzeugen. Wir haben verschiedene Bibliotheken wie jsPDF, pdfmake und React-PDF erkundet, von denen jede ihre eigenen Stärken und einzigartigen Funktionen hat.
Mit IronPDFs einfacher Integration in JavaScript-Frameworks und -Bibliotheken, hervorragender Dokumentation und reaktionsschnellem technischen Support können Entwickler im Handumdrehen loslegen, was es zu einer Top-Wahl für die Erstellung professioneller PDFs in Node.js-Anwendungen macht.
IronPDF bietet eine kostenlose Testversion mit vollem Funktionsumfang an. Es ist auch in anderen Sprachen wie C# .NET, Java und Python verfügbar. Besuchen Sie die IronPDF-Website für weitere Details.




