Generating PDF Reports in TypeScript with the pdf-lib Library
In modern web development, there is often a need to create documents in PDF format — from simple receipts and invoices to complex analytical reports with charts. TypeScript, as a strictly typed extension of JavaScript, provides excellent capabilities for working with PDFs. Among the many libraries, pdf-lib stands out — a powerful, fully client-side tool that requires no server dependencies.
In this article, we will thoroughly examine how to use pdf-lib for generating PDF reports in TypeScript, including installation, basic operations, and advanced techniques.
What is pdf-lib?
pdf-lib is an open-source library written in TypeScript that allows you to create and edit PDF documents directly in the browser or in a Node.js environment. Its main advantages:
- Full TypeScript support (all types are included)
- Works without external dependencies (no need for Puppeteer or wkhtmltopdf)
- Ability to work with existing PDFs (merging, inserting pages)
- Support for Cyrillic and any fonts
- Embedding images (PNG, JPG, SVG)
- Creating forms and annotations
Installation and Setup
Installation is done using the standard package manager:
npm install pdf-lib# oryarn add pdf-libNo additional steps are required for working with TypeScript — all types are already included in the package.
Basic Example: Creating a Simple Report
Let's look at creating a minimal PDF document with a title and text:
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
async function createSimpleReport() { // 1. Create a new PDF document const pdfDoc = await PDFDocument.create(); // 2. Add a page (A4 format: 595.28 x 841.89 points) const page = pdfDoc.addPage([595.28, 841.89]); // 3. Embed a standard font const font = await pdfDoc.embedFont(StandardFonts.Helvetica); // 4. Draw the title page.drawText('Sales Report for Q1 2025', { x: 50, y: 800, size: 24, font: font, color: rgb(0, 0.2, 0.6), }); // 5. Draw the main text page.drawText('Total revenue: 1,234,567 RUB', { x: 50, y: 760, size: 14, font: font, color: rgb(0, 0, 0), }); // 6. Save the document as Uint8Array const pdfBytes = await pdfDoc.save(); return pdfBytes;}Working with Tables and Data
Creating tabular reports requires a bit more logic. Here is an example of generating a table with data:
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
interface SaleRecord { product: string; quantity: number; price: number;}
async function createTableReport(data: SaleRecord[]) { const pdfDoc = await PDFDocument.create(); const page = pdfDoc.addPage([595.28, 841.89]); const font = await pdfDoc.embedFont(StandardFonts.Helvetica); const boldFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold); const startX = 50; let startY = 800; const rowHeight = 25; const colWidths = [200, 100, 100, 100]; // Name, Quantity, Price, Total // Table header const headers = ['Name', 'Quantity', 'Price', 'Total']; page.drawText(headers[0], { x: startX, y: startY, size: 12, font: boldFont }); page.drawText(headers[1], { x: startX + colWidths[0], y: startY, size: 12, font: boldFont }); page.drawText(headers[2], { x: startX + colWidths[0] + colWidths[1], y: startY, size: 12, font: boldFont }); page.drawText(headers[3], { x: startX + colWidths[0] + colWidths[1] + colWidths[2], y: startY, size: 12, font: boldFont }); startY -= rowHeight; // Data rows for (const record of data) { const total = reco