DEV Community

IronSoftware
IronSoftware

Posted on

Migrating from Puppeteer/Playwright to IronPDF in .NET

Puppeteer and Playwright are browser automation tools that happen to generate PDFs. IronPDF is a purpose-built PDF library designed for production .NET applications.

If you're using Puppeteer-Sharp or Playwright for HTML-to-PDF conversion in .NET, here's why and how to migrate to IronPDF.

Why Migrate from Puppeteer/Playwright to IronPDF?

1. Puppeteer/Playwright Aren't PDF Libraries

Puppeteer and Playwright are browser automation tools designed for:

  • End-to-end testing
  • Web scraping
  • Screenshot capture
  • Form automation

PDF generation is a side feature, not the primary focus.

IronPDF is a dedicated PDF library built for:

  • HTML-to-PDF conversion
  • PDF manipulation (merge, split, forms)
  • Digital signatures
  • PDF/A compliance
  • Production-grade performance

2. Deployment Complexity

Puppeteer-Sharp/Playwright require:

  • 300MB+ Chromium browser binaries
  • Browser installation on every server
  • Managing browser versions
  • Platform-specific browser downloads

Docker deployment (Puppeteer-Sharp):

FROM mcr.microsoft.com/dotnet/aspnet:8.0

# Install Chromium and dependencies (huge image size increase)
RUN apt-get update && apt-get install -y \
    wget \
    gnupg \
    ca-certificates \
    fonts-liberation \
    libasound2 \
    libatk-bridge2.0-0 \
    libatk1.0-0 \
    libc6 \
    libcairo2 \
    libcups2 \
    libdbus-1-3 \
    libexpat1 \
    libfontconfig1 \
    libgbm1 \
    libgcc1 \
    libglib2.0-0 \
    libgtk-3-0 \
    libnspr4 \
    libnss3 \
    libpango-1.0-0 \
    libpangocairo-1.0-0 \
    libstdc++6 \
    libx11-6 \
    libx11-xcb1 \
    libxcb1 \
    libxcomposite1 \
    libxcursor1 \
    libxdamage1 \
    libxext6 \
    libxfixes3 \
    libxi6 \
    libxrandr2 \
    libxrender1 \
    libxss1 \
    libxtst6 \
    lsb-release \
    xdg-utils \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY . .
ENTRYPOINT ["dotnet", "MyApp.dll"]
Enter fullscreen mode Exit fullscreen mode

IronPDF Docker deployment:

FROM mcr.microsoft.com/dotnet/aspnet:8.0

RUN apt-get update && apt-get install -y \
    libc6-dev \
    libgdiplus \
    libx11-dev \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY . .
ENTRYPOINT ["dotnet", "MyApp.dll"]
Enter fullscreen mode Exit fullscreen mode

IronPDF's Docker image is 200MB+ smaller.

3. Performance Overhead

Puppeteer/Playwright spawn fresh browser processes for every PDF generation (or maintain heavy browser contexts).

Puppeteer-Sharp example:

using PuppeteerSharp;
// Install via NuGet: Install-Package PuppeteerSharp

for (int i = 0; i < 100; i++)
{
    // Launches new browser process each time!
    using var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true });
    using var page = await browser.NewPageAsync();
    await page.SetContentAsync($"<h1>Document {i}</h1>");
    await page.PdfAsync($"output-{i}.pdf");
}
Enter fullscreen mode Exit fullscreen mode

Each iteration:

  1. Spawns browser process (~500ms overhead)
  2. Initializes browser context
  3. Renders HTML
  4. Generates PDF
  5. Shuts down browser

This is slow for batch processing.

IronPDF reuses the rendering engine:

using IronPdf;
// Install via NuGet: Install-Package IronPdf

var renderer = new ChromePdfRenderer(); // Create once

for (int i = 0; i < 100; i++)
{
    var pdf = renderer.RenderHtmlAsPdf($"<h1>Document {i}</h1>");
    pdf.SaveAs($"output-{i}.pdf");
}
Enter fullscreen mode Exit fullscreen mode

First render: ~2.8 seconds (Chromium initialization)
Subsequent renders: <1 second each

IronPDF is 5-10x faster for batch operations.

4. API Complexity

Puppeteer/Playwright APIs are verbose and designed for browser automation, not PDF generation.

Playwright example:

using Microsoft.Playwright;
// Install via NuGet: Install-Package Microsoft.Playwright

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

await page.SetContentAsync("<h1>Hello World</h1>");
await page.PdfAsync(new()
{
    Path = "output.pdf",
    Format = "A4",
    PrintBackground = true
});

await browser.CloseAsync();
Enter fullscreen mode Exit fullscreen mode

IronPDF equivalent:

using IronPdf;
// Install via NuGet: Install-Package IronPdf

var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1>");
pdf.SaveAs("output.pdf");
Enter fullscreen mode Exit fullscreen mode

80% less code, clearer intent.

5. No PDF Manipulation Features

Puppeteer/Playwright can only generate PDFs. They can't:

  • Merge multiple PDFs
  • Split PDFs into pages
  • Extract text from existing PDFs
  • Fill PDF forms
  • Add digital signatures
  • Apply watermarks to existing PDFs

IronPDF handles all of this:

using IronPdf;
// Install via NuGet: Install-Package IronPdf

// Merge PDFs
var pdf1 = PdfDocument.FromFile("doc1.pdf");
var pdf2 = PdfDocument.FromFile("doc2.pdf");
var merged = PdfDocument.Merge(pdf1, pdf2);

// Extract text
var text = pdf1.ExtractAllText();

// Fill forms
pdf1.Form.SetFieldValue("name", "John Doe");

// Add watermark
pdf1.ApplyWatermark("<h1 style='color: rgba(0,0,0,0.2);'>DRAFT</h1>");
Enter fullscreen mode Exit fullscreen mode

Migration: Puppeteer-Sharp to IronPDF

Step 1: Replace Basic HTML-to-PDF

Before (Puppeteer-Sharp):

using PuppeteerSharp;
// Install via NuGet: Install-Package PuppeteerSharp

var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true });
var page = await browser.NewPageAsync();
await page.SetContentAsync("<h1>Invoice</h1><p>Total: $500</p>");
await page.PdfAsync("invoice.pdf");
await browser.CloseAsync();
Enter fullscreen mode Exit fullscreen mode

After (IronPDF):

using IronPdf;
// Install via NuGet: Install-Package IronPdf

var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf("<h1>Invoice</h1><p>Total: $500</p>");
pdf.SaveAs("invoice.pdf");
Enter fullscreen mode Exit fullscreen mode

Benefits:

  • No browser lifecycle management
  • Faster (no process spawning)
  • Cleaner code

Step 2: Replace URL-to-PDF Conversion

Before (Puppeteer-Sharp):

using PuppeteerSharp;
// Install via NuGet: Install-Package PuppeteerSharp

var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true });
var page = await browser.NewPageAsync();
await page.GoToAsync("https://example.com");
await page.PdfAsync("webpage.pdf");
await browser.CloseAsync();
Enter fullscreen mode Exit fullscreen mode

After (IronPDF):

using IronPdf;
// Install via NuGet: Install-Package IronPdf

var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://example.com");
pdf.SaveAs("webpage.pdf");
Enter fullscreen mode Exit fullscreen mode

Step 3: Replace Custom Page Options

Before (Playwright):

using Microsoft.Playwright;
// Install via NuGet: Install-Package Microsoft.Playwright

var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();

await page.SetContentAsync("<h1>Content</h1>");
await page.PdfAsync(new()
{
    Path = "output.pdf",
    Format = "A4",
    Landscape = true,
    Margin = new() { Top = "20mm", Bottom = "20mm", Left = "15mm", Right = "15mm" },
    PrintBackground = true
});

await browser.CloseAsync();
Enter fullscreen mode Exit fullscreen mode

After (IronPDF):

using IronPdf;
using IronPdf.Rendering;
// Install via NuGet: Install-Package IronPdf

var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;
renderer.RenderingOptions.PaperOrientation = PdfPaperOrientation.Landscape;
renderer.RenderingOptions.MarginTop = 20;
renderer.RenderingOptions.MarginBottom = 20;
renderer.RenderingOptions.MarginLeft = 15;
renderer.RenderingOptions.MarginRight = 15;
renderer.RenderingOptions.PrintHtmlBackgrounds = true;

var pdf = renderer.RenderHtmlAsPdf("<h1>Content</h1>");
pdf.SaveAs("output.pdf");
Enter fullscreen mode Exit fullscreen mode

Strongly-typed options, compile-time validation.

Step 4: Replace Headers and Footers

Before (Puppeteer-Sharp):

using PuppeteerSharp;
// Install via NuGet: Install-Package PuppeteerSharp

var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true });
var page = await browser.NewPageAsync();
await page.SetContentAsync("<h1>Content</h1>");

await page.PdfAsync(new PdfOptions
{
    Path = "output.pdf",
    DisplayHeaderFooter = true,
    HeaderTemplate = "<div style='font-size: 10px;'>Header</div>",
    FooterTemplate = "<div style='font-size: 10px;'><span class='pageNumber'></span> of <span class='totalPages'></span></div>"
});

await browser.CloseAsync();
Enter fullscreen mode Exit fullscreen mode

After (IronPDF):

using IronPdf;
// Install via NuGet: Install-Package IronPdf

var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.HtmlHeader = new HtmlHeaderFooter
{
    HtmlFragment = "<div style='font-size: 10px;'>Header</div>"
};
renderer.RenderingOptions.HtmlFooter = new HtmlHeaderFooter
{
    HtmlFragment = "<div style='font-size: 10px;'>Page {page} of {total-pages}</div>"
};

var pdf = renderer.RenderHtmlAsPdf("<h1>Content</h1>");
pdf.SaveAs("output.pdf");
Enter fullscreen mode Exit fullscreen mode

IronPDF's merge fields ({page}, {total-pages}) are more intuitive than Puppeteer's class-based approach.

Step 5: Replace JavaScript Execution Wait

Before (Puppeteer-Sharp):

using PuppeteerSharp;
// Install via NuGet: Install-Package PuppeteerSharp

var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true });
var page = await browser.NewPageAsync();
await page.SetContentAsync(@"
<div id='app'>Loading...</div>
<script>
    setTimeout(() => {
        document.getElementById('app').innerHTML = '<h1>Loaded!</h1>';
    }, 500);
</script>");

await page.WaitForTimeoutAsync(1000); // Wait for JavaScript
await page.PdfAsync("output.pdf");
await browser.CloseAsync();
Enter fullscreen mode Exit fullscreen mode

After (IronPDF):

using IronPdf;
// Install via NuGet: Install-Package IronPdf

var html = @"
<div id='app'>Loading...</div>
<script>
    setTimeout(() => {
        document.getElementById('app').innerHTML = '<h1>Loaded!</h1>';
    }, 500);
</script>";

var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.RenderDelay = 1000; // Wait for JavaScript
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
Enter fullscreen mode Exit fullscreen mode

Performance Comparison

Benchmark (100 simple HTML documents):

Library Time Notes
Puppeteer-Sharp ~120 seconds Spawns browser process each time
Playwright ~110 seconds Similar overhead
IronPDF ~60 seconds Reuses Chromium engine

IronPDF is 2x faster for batch operations.

Cost Comparison

Puppeteer-Sharp/Playwright IronPDF
License cost Free (Apache 2.0) $749/developer
Docker image size +500MB (Chromium + deps) +100MB
Deployment complexity High (browser binaries) Low (NuGet package)
Performance Moderate (process spawning) Fast (reused engine)
PDF manipulation ❌ No ✅ Yes
Support Community (GitHub) 24/5 commercial support

IronPDF costs $749 but saves deployment complexity and developer time.

When to Stay with Puppeteer/Playwright

Don't migrate if:

  1. You're already using Puppeteer/Playwright for testing and PDF generation is a side feature
  2. Budget is $0 and you can tolerate complexity
  3. You need advanced browser automation (form filling, screenshots, scraping) alongside PDF generation

Migrate to IronPDF if:

  1. PDF generation is your primary use case (not browser automation)
  2. Deployment simplicity matters (smaller Docker images, easier configuration)
  3. Performance matters (batch processing, high throughput)
  4. You need PDF manipulation (merge, split, forms, signatures)
  5. Commercial support is valuable (SLA, guaranteed bug fixes)

Migration Checklist

Audit current Puppeteer/Playwright usage (HTML conversion, screenshots, automation)

If 80%+ is PDF generation → migrate to IronPDF

Install IronPDF via NuGet (Install-Package IronPdf)

Replace browser launch/close with ChromePdfRenderer (reusable)

Migrate PDF options to RenderingOptions

Remove browser binary installation from Docker/deployment

Update Docker images (smaller, simpler)

Performance test with real workloads

License IronPDF for production use

The Bottom Line

Puppeteer and Playwright are browser automation tools that happen to generate PDFs.

IronPDF is a purpose-built PDF library designed for production .NET applications.

If PDF generation is your primary use case:

  • IronPDF is 2x faster
  • 80% less code
  • 200MB+ smaller Docker images
  • No browser process management
  • Comprehensive PDF manipulation features
  • Commercial support

For $749/developer, you eliminate deployment complexity, improve performance, and gain production-grade PDF capabilities.


Written by Jacob Mellor, CTO at Iron Software. Jacob created IronPDF and leads a team of 50+ engineers building .NET document processing libraries.

Top comments (0)