Loading...

How Do You Generate PDFs with Playwright?

Want to create PDFs programmatically? Learn how to generate PDFs from web pages using Playwright with real-world examples and best practices.

Back

In today's digital landscape, generating PDFs programmatically is a common requirement for many web applications. Whether you're creating invoices, reports, or documentation, Playwright offers a powerful and flexible solution. Let's explore how to leverage Playwright's capabilities for PDF generation with practical examples.

Note: If you're looking for a managed solution, screenshotsapi.dev offers professional PDF generation services with features like HTML to PDF conversion, PDF merging, and watermarking - all through a simple API without managing your own infrastructure.

Why Choose Playwright for PDF Generation?

Before diving into the implementation, let's understand why Playwright stands out for PDF generation:

  1. Cross-browser Support: Unlike many alternatives, Playwright supports multiple browser engines
  2. Modern API: Clean, promise-based API with excellent TypeScript support
  3. Powerful Options: Fine-grained control over PDF output including headers, footers, and styling
  4. Performance: Efficient handling of modern web technologies and JavaScript-heavy pages

Getting Started

First, let's set up our project with Playwright:

# Install Playwright
npm install playwright
 
# Or using yarn
yarn add playwright

Basic PDF Generation

Here's a simple example to get started:

import { chromium } from 'playwright';
 
async function generateBasicPDF(url: string) {
    const browser = await chromium.launch();
    try {
        const page = await browser.newPage();
        await page.goto(url, {
            waitUntil: 'networkidle'
        });
        
        await page.pdf({
            path: 'output.pdf',
            format: 'A4',
            printBackground: true
        });
    } finally {
        await browser.close();
    }
}
 
// Usage
generateBasicPDF('https://example.com');

Real-World Examples

1. Professional Invoice Generation

interface InvoiceData {
    invoiceNumber: string;
    customerName: string;
    items: Array<{
        description: string;
        amount: number;
    }>;
    total: number;
}
 
async function generateInvoicePDF(data: InvoiceData) {
    const browser = await chromium.launch();
    try {
        const page = await browser.newPage();
        
        const html = `
            <html>
                <head>
                    <style>
                        body { 
                            font-family: Arial, sans-serif; 
                            padding: 20px;
                            color: #333;
                        }
                        .invoice-header { 
                            text-align: center; 
                            margin-bottom: 30px;
                            border-bottom: 2px solid #eee;
                            padding-bottom: 20px;
                        }
                        .customer-info { 
                            margin-bottom: 20px;
                            background: #f9f9f9;
                            padding: 15px;
                            border-radius: 5px;
                        }
                        .items-table { 
                            width: 100%; 
                            border-collapse: collapse;
                            margin-top: 20px;
                        }
                        .items-table th, .items-table td { 
                            border: 1px solid #ddd; 
                            padding: 12px; 
                            text-align: left;
                        }
                        .items-table th {
                            background: #f5f5f5;
                        }
                        .total-row {
                            font-weight: bold;
                            background: #f0f0f0;
                        }
                    </style>
                </head>
                <body>
                    <div class="invoice-header">
                        <h1>Invoice #${data.invoiceNumber}</h1>
                        <p>Date: ${new Date().toLocaleDateString()}</p>
                    </div>
                    <div class="customer-info">
                        <h2>Customer: ${data.customerName}</h2>
                    </div>
                    <table class="items-table">
                        <thead>
                            <tr>
                                <th>Description</th>
                                <th>Amount</th>
                            </tr>
                        </thead>
                        <tbody>
                            ${data.items.map(item => `
                                <tr>
                                    <td>${item.description}</td>
                                    <td>$${item.amount.toFixed(2)}</td>
                                </tr>
                            `).join('')}
                        </tbody>
                        <tfoot>
                            <tr class="total-row">
                                <td>Total</td>
                                <td>$${data.total.toFixed(2)}</td>
                            </tr>
                        </tfoot>
                    </table>
                </body>
            </html>
        `;
 
        await page.setContent(html);
        
        await page.pdf({
            path: `invoice-${data.invoiceNumber}.pdf`,
            format: 'A4',
            margin: {
                top: '20px',
                bottom: '20px',
                left: '20px',
                right: '20px'
            },
            printBackground: true
        });
    } finally {
        await browser.close();
    }
}

2. Advanced PDF with Headers and Footers

async function generateProfessionalPDF(url: string, options: {
    title: string;
    author: string;
    footerTemplate?: string;
}) {
    const browser = await chromium.launch();
    try {
        const page = await browser.newPage();
        await page.goto(url, { waitUntil: 'networkidle' });
 
        await page.pdf({
            path: 'professional-doc.pdf',
            format: 'A4',
            displayHeaderFooter: true,
            headerTemplate: `
                <div style="
                    font-size: 10px;
                    padding: 10px 20px;
                    border-bottom: 1px solid #ddd;
                    width: 100%;
                    display: flex;
                    justify-content: space-between;
                ">
                    <span>${options.title}</span>
                    <span>Author: ${options.author}</span>
                    <span class="date"></span>
                </div>
            `,
            footerTemplate: options.footerTemplate || `
                <div style="
                    font-size: 10px;
                    padding: 10px 20px;
                    text-align: center;
                    width: 100%;
                ">
                    <span>Page </span>
                    <span class="pageNumber"></span>
                    <span> of </span>
                    <span class="totalPages"></span>
                </div>
            `,
            margin: {
                top: '100px',
                bottom: '100px',
                right: '30px',
                left: '30px'
            }
        });
    } finally {
        await browser.close();
    }
}

Best Practices

1. Resource Management

  • Use try/finally blocks to ensure browser closure
  • Implement browser instance pooling for high-volume scenarios
  • Handle cleanup in error scenarios

2. Content Preparation

  • Ensure all content is fully loaded
  • Wait for dynamic content to render
  • Handle fonts and styling consistently
  • Consider page breaks and layout

3. Error Handling

  • Handle network timeouts
  • Manage memory constraints
  • Provide fallback options
  • Log errors appropriately

4. Performance Optimization

  • Reuse browser instances when possible
  • Optimize page content for PDF generation
  • Consider caching frequently generated PDFs
  • Use appropriate viewport sizes

Common Challenges and Solutions

Challenge 1: Dynamic Content

When dealing with JavaScript-heavy pages, ensure content is fully rendered before PDF generation.

Challenge 2: Styling Consistency

Use CSS print media queries and specific PDF styling to maintain consistent output.

Challenge 3: Headers and Footers

Implement professional headers and footers while maintaining proper page margins and content flow.

Challenge 4: Large Documents

Handle memory constraints and optimize performance for large documents.

Additional Resources

Want to learn more about Playwright? Check out our other guides:

Happy PDF generating with Playwright! 🚀

Written by

Durgaprasad Budhwani

At

Tue Jan 02 2024