pdforge

Product

Resources

Integrations

pdf libraries

pdf libraries

C#

C#

How to Generate PDF from HTML with Playwright using C#

Marcelo Abreu, founder of pdforge

Marcelo | Founder

Marcelo | Founder

Mar 24, 2025

Mar 24, 2025

Introduction to PDF Generation with Playwright

In the realm of C# development, converting HTML to PDF is a common requirement, particularly for SaaS applications that generate dynamic reports, invoices, or user-centric documents. Playwright, an advanced automation library, offers a robust and efficient solution for this task. This comprehensive guide explores the process of utilizing Playwright in C# to achieve high-fidelity PDF generation from HTML content.

Playwright also has well-written, robust documentation that you can use if you run into any trouble.


If you're using other programming languages, check out our other guides:

Comparison Between Playwright and Other C# PDF Libraries

When it comes to PDF generation in C#, several libraries are available. Here’s how Playwright compares to some popular options:

Download comparison between pdf libraries using nuget trends

PdfSharp: Ideal for creating PDFs from scratch but lacks direct HTML to PDF conversion capabilities.

PuppeteerSharp: A port of Puppeteer for C#, allowing control over Chrome or Chromium browsers. It handles HTML to PDF conversion but can be resource-intensive and may have compatibility issues.

iTextSharp: A comprehensive library for PDF manipulation with extensive features, but it has a steep learning curve and licensing considerations for commercial use.

Playwright stands out due to:

  • Modern Web Rendering: Offers high-fidelity rendering of complex, modern web pages, including JavaScript execution and CSS styling.

  • Cross-Browser Support: Works with Chromium, Firefox, and WebKit, ensuring consistent PDF output across browsers.

  • Performance: Designed for efficiency, making it suitable for both development and production environments.

Guide to generate pdf from html using C# Playwright
Guide to generate pdf from html using C# Playwright

Setting Up Playwright for HTML to PDF Conversion

Installing Playwright and Its Browser Dependencies for .NET Projects

Before starting, ensure that Node.js and the .NET SDK are installed on your system.

1. Install Node.js: Download and install from the official Node.js website.

2. Create a New .NET Project:

Open a terminal and run:

dotnet new console -o HtmlToPdfExample
cd

3. Add Playwright Package:

dotnet add package Microsoft.Playwright

4. Install Playwright Browsers:

npx playwright install

Step-by-step: Generating PDFs with Playwright

Method 01: Generating PDF from a URL

To generate a PDF directly from a webpage URL, use the following straightforward approach with Playwright in C#:

Add this complete snippet to your Program.cs file:

using System;
using System.Threading.Tasks;
using Microsoft.Playwright;
class Program
{
    public static async Task Main(string[] args)
    {
        using var playwright = await Playwright.CreateAsync();
        await using var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions { Headless = true });
        var context = await browser.NewContextAsync();
        var page = await context.NewPageAsync();
        // Navigate to the target URL
        await page.GotoAsync("https://www.example.com");
        await page.WaitForLoadStateAsync(LoadState.NetworkIdle);
        // Generate PDF
        await page.PdfAsync(new PagePdfOptions
        {
            Path = "website.pdf",
            Format = "A4",
            PrintBackground = true,
            Margin = new Margin { Top = "20mm", Bottom = "20mm", Left = "10mm", Right = "10mm" }
        });
        Console.WriteLine("Successfully generated PDF from URL."

This method launches Playwright, loads the webpage, waits until it's fully loaded, and saves the page as a high-quality PDF file (website.pdf).

Method 02: Generating PDF from HTML Content

This approach demonstrates generating PDFs from static or dynamic HTML content by adding your html content into your Program.cs file to convert the HTML into a PDF:

using System;
using System.Threading.Tasks;
using Microsoft.Playwright;

class Program
{
    public static async Task Main(string[] args)
    {
        var htmlContent = @"
        <html>
            <head>
                <meta charset='UTF-8'>
                <title>Simple PDF</title>
            </head>
            <body style='font-family: Arial, sans-serif; padding: 2rem;'>
                <h1>Hello from Playwright!</h1>
                <p>This PDF was generated using setContentAsync.</p>
            </body>
        </html>";

        using var playwright = await Playwright.CreateAsync();
        await using var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions { Headless = true });
        var context = await browser.NewContextAsync();
        var page = await context.NewPageAsync();

        // Inject raw HTML directly into the page
        await page.SetContentAsync(htmlContent);
        await page.WaitForLoadStateAsync(LoadState.NetworkIdle);

        await page.PdfAsync(new PagePdfOptions
        {
            Path = "simple.pdf",
            Format = "A4",
            PrintBackground = true,
            Margin = new Margin { Top = "20mm", Bottom = "20mm", Left = "10mm", Right = "10mm" }
        });

        Console.WriteLine("PDF generated successfully using setContentAsync."

Adding Headers, Footers, and Page Numbers with Playwright

Customize headers and footers using HTML templates within the PagePdfOptions:

await page.PdfAsync(new PagePdfOptions
{
    Path = "invoice_with_header_footer.pdf",
    DisplayHeaderFooter = true,
    HeaderTemplate = "<div style='font-size:12px; text-align:center; width:100%;'>My SaaS Application</div>",
    FooterTemplate = "<div style='font-size:12px; text-align:center; width:100%;'>Page <span class=\"pageNumber\"></span> of <span class=\"totalPages\"></span></div>",
    Margin = new Margin { Top = "40mm", Bottom = "20mm", Left = "10mm", Right = "10mm" },
    Format = "A4",
    PrintBackground = true

HTML Template Engines in C#

To manage dynamic data in your HTML templates more efficiently, consider using an HTML template engine. Popular options in C# include:

Razor: The default view engine for ASP.NET MVC, allowing you to embed C# code within HTML.

Scriban: A fast and lightweight templating language with simple syntax.

DotLiquid: A .NET port of the popular Ruby Liquid templating engine.

Using Scriban to Generate Dynamic HTML Templates

To efficiently manage dynamic data in your HTML templates, using a templating engine like Scriban is highly beneficial. Here's how to integrate Scriban with Playwright:

First, install Scriban using NuGet:

Next, create your html template file using Scriban syntax:

<!-- invoice.html -->
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Invoice #{{ invoice.number }}</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
        .invoice-box { max-width: 800px; margin: auto; border: 1px solid #eee; padding: 20px; }
        .invoice-header { text-align: center; margin-bottom: 50px; }
        .invoice-details { width: 100%; margin-bottom: 30px; }
        .invoice-details th, .invoice-details td { padding: 10px; border-bottom: 1px solid #eee; text-align: left; }
        .invoice-items { width: 100%; border-collapse: collapse; }
        .invoice-items th, .invoice-items td { border: 1px solid #eee; padding: 10px; text-align: left; }
        .total { text-align: right; margin-top: 20px; font-size: 18px; }
    </style>
</head>
<body>
    <div class="invoice-box">
        <div class="invoice-header">
            <h1>Invoice #{{ invoice.number }}</h1>
            <p>Date: {{ invoice.date }}</p>
            <p>Due Date: {{ invoice.due_date }}</p>
        </div>
        <table class="invoice-details">
            <tr>
                <th>Billed To:</th>
                <td>{{ invoice.client.name | html.escape }}</td>
            </tr>
            <tr>
                <th>Email:</th>
                <td>{{ invoice.client.email | html.escape }}</td>
            </tr>
            <tr>
                <th>Address:</th>
                <td>{{ invoice.client.address | html.escape }}</td>
            </tr>
        </table>
        <table class="invoice-items">
            <tr>
                <th>Description</th>
                <th>Quantity</th>
                <th>Unit Price</th>
                <th>Amount</th>
            </tr>
            {{ for item in invoice.items }}
            <tr>
                <td>{{ item.description | html.escape }}</td>
                <td>{{ item.quantity }}</td>
                <td>${{ item.unit_price | math.format "F2" }}</td>
                <td>${{ item.amount | math.format "F2" }}</td>
            </tr>
            {{ end }}
        </table>
        <div class="total">
            <strong>Total: ${{ invoice.total_amount | math.format "F2" }}</strong>
        </div>
    </div>
</body>
</html>

Then, create or update your Program.cs file with this complete example:

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Playwright;
using Scriban;

class Program
{
    public static async Task Main(string[] args)
    {
        var templateContent = File.ReadAllText("invoice.html");
        var template = Template.Parse(templateContent);

        var data = new
        {
            invoice = new
            {
                number = "INV-1001",
                date = DateTime.Now.ToString("yyyy-MM-dd"),
                due_date = DateTime.Now.AddDays(30).ToString("yyyy-MM-dd"),
                client = new
                {
                    name = "Acme Corp",
                    email = "contact@acme.com",
                    address = "123 Business Rd, Business City, BC 54321"
                },
                items = new[]
                {
                    new { description = "Web Development Services", quantity = 1, unit_price = 1500.00, amount = 1500.00 },
                    new { description = "Hosting Services", quantity = 12, unit_price = 10.00, amount = 120.00 }
                },
                total_amount = 1620.00
            }
        };

        var htmlContent = template.Render(data);

        string tempHtmlPath = Path.Combine(Path.GetTempPath(), "dynamic_invoice.html");
        File.WriteAllText(tempHtmlPath, htmlContent);

        using var playwright = await Playwright.CreateAsync();
        await using var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions { Headless = true });
        var context = await browser.NewContextAsync();
        var page = await context.NewPageAsync();

        await page.GotoAsync($"file://{tempHtmlPath}");
        await page.WaitForLoadStateAsync(LoadState.NetworkIdle);

        await page.PdfAsync(new PagePdfOptions
        {
            Path = "dynamic_invoice.pdf",
            Format = "A4",
            PrintBackground = true,
            Margin = new Margin { Top = "20mm", Bottom = "20mm", Left = "10mm", Right = "10mm" }
        });

        Console.WriteLine("PDF generated successfully using Scriban template."

Using Scriban makes your PDF generation process significantly more flexible and scalable, easily accommodating dynamic and complex document structures.

Scaling PDF Generation with Playwright and Serverless Architectures

Scaling PDF generation can be challenging when handling high volumes. Deploying your PDF generation logic as a serverless service offers several benefits. You can use popular providers such as AWS Lambda or Google Cloud Functions to make it serverless.

Benefits of Serverless PDF Generation

  • Scalability: Automatically handles increased load without manual intervention.

  • Cost-Effective: Pay only for the actual compute time used.

  • Maintenance: Reduced operational overhead as there’s no need to manage servers.

By deploying to AWS Lambda or Google Cloud Functions, your application can efficiently handle spikes in PDF generation requests, ensuring reliable performance for your users.

We have a full guide on how to deploy playwright on AWS Lambda here.

Alternative: Scaling PDF Generation with Third-Party APIs

homepage of pdforge

For larger SaaS platforms requiring automated PDF generation at scale, integrating a PDF Generation API like pdforge can offload the heavy lifting. This approach is ideal for SaaS platforms with high volumes of PDF requests.

With pdforge, you can create beautiful reports with flexible layouts and complex components with an easy-to-use opinionated no-code builder. Let the AI do the heavy lifting by generating your templates, creating custom components or even filling all the variables for you.

You can handle high-volume PDF generation from a single backend call.

Here’s an example of how to generate pdf with pdforge via an API call:

using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace PdfApiIntegration
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var client = new HttpClient();
            client.DefaultRequestHeaders.Add("Authorization", "Bearer your-api-key");
            var requestBody = new
            {
                templateId = "your-template",
                data = new { html = "your-html" }
            };
            var content = new StringContent(
                Newtonsoft.Json.JsonConvert.SerializeObject(requestBody),
                Encoding.UTF8,
                "application/json"
            );
            var response = await client.PostAsync("https://api.pdforge.com/v1/pdf/sync", content);
            if (response.IsSuccessStatusCode)
            {
                var pdfBytes = await response.Content.ReadAsByteArrayAsync();
                File.WriteAllBytes("invoice.pdf", pdfBytes);
                Console.WriteLine("PDF generated using PDFForge API.");
            }
            else
            {
                Console.WriteLine("Error generating PDF: " + response.ReasonPhrase

You can create your account, experience our no-code builder and create your first layout template without any upfront payment clicking here.

Conclusion

When deciding on a method for generating PDFs from HTML in your SaaS application:

Use Playwright when you need precise control over rendering and can manage the necessary infrastructure. It excels at generating PDFs from complex, dynamic web content. You can create a powerful and flexible solution for converting HTML to PDF.

Consider Other Libraries like PuppeteerSharp or iTextSharp if they better align with your project’s requirements or if you’re already familiar with them.

Opt for third-party PDF Generation APIs like pdforge if you don't want to waste time maintaining pdfs layouts and their infrastructure or if you don't want to keep track of best practices to generate PDFs at scale. It will save you hours of work and deliver a high quality pdf layout.

Generating pdfs at scale can be annoying!

Generating pdfs at scale can be annoying!

Let us help you make it easier while you focus on what trully matters for your company.

Let us help you make it easier while you focus on what trully matters for your company.

7-day free trial

7-day free trial

Table of contents