How to Migrate from Playwright to IronPDF in C#
Migrating from Playwright for .NET to IronPDF transforms your PDF generation workflow from a testing-centric browser automation tool to a purpose-built PDF library. This guide provides a complete, step-by-step migration path that eliminates complex async patterns, browser lifecycle management, and 400MB+ browser downloads while providing better performance and professional PDF features.
Why Migrate from Playwright to IronPDF
Understanding Playwright for .NET
Playwright for .NET is a part of Microsoft's family of browser automation tools; it is structured around delivering comprehensive testing capabilities across Chromium, Firefox, and WebKit. The library embraces a "testing-first" design, which means its primary focus is on scenarios that involve browser-based testing. Although Playwright supports PDF generation, this functionality is more of a supplementary feature and does not offer the granular configuration seen in dedicated PDF tools.
Playwright for .NET is primarily a browser automation and testing framework where PDF generation is a secondary feature. Like PuppeteerSharp, Playwright generates PDFs using the browser's print-to-PDF functionality—equivalent to hitting Ctrl+P. This produces print-ready output optimized for paper, which differs from screen rendering.
The Testing Framework Problem
Playwright was designed for end-to-end testing, not document generation. This creates fundamental issues when using it for PDFs:
400MB+ browser downloads required before first use. Playwright's default configuration involves downloading multiple browsers, which can be a consideration for environments with strict resource constraints.
Complex async patterns with browser contexts and page management. Developers must gain familiarity with browser contexts and page management, along with proper disposal practices.
Testing-first architecture not optimized for document generation.
Print-to-PDF limitations equivalent to Ctrl+P browser print. Layouts may reflow, backgrounds may be omitted by default, and output is paginated for printing.
No PDF/A or PDF/UA support for accessibility compliance. Playwright cannot produce PDF/A (archival) or PDF/UA (accessibility) compliant documents. For Section 508, EU accessibility directives, or long-term archival requirements, you'll need a dedicated PDF library.
- Resource-heavy operations requiring full browser instances.
Playwright vs IronPDF Performance Comparison
| Metric | Playwright | IronPDF |
|---|---|---|
| Primary Purpose | Browser testing | PDF generation |
| Browser Download | 400MB+ (Chromium, Firefox, WebKit) | Built-in optimized engine |
| First Render (Cold Start) | 4.5 seconds | 2.8 seconds |
| Subsequent Renders | 3.8-4.1 seconds | 0.8-1.2 seconds |
| Memory per Conversion | 280-420MB | 80-120MB |
| API Complexity | Async browser/context/page lifecycle | Synchronous one-liners |
| Initialization | playwright install + CreateAsync + LaunchAsync | new ChromePdfRenderer() |
| PDF/A Support | Not available | Full support |
| PDF/UA Accessibility | Not available | Full support |
| Digital Signatures | Not available | Full support |
| PDF Editing | Not available | Merge, split, stamp, edit |
| Professional Support | Community | Commercial with SLA |
IronPDF was built with a focus on PDF generation. Unlike the testing-centric Playwright, IronPDF provides a variety of document-centric API features. It relies on a single optimized Chromium instance, favoring efficiency and offering both synchronous and asynchronous operations. This results in a simpler mental model and workflow for developers who require PDF functionalities.
For teams planning .NET 10 and C# 14 adoption through 2025 and 2026, IronPDF provides a purpose-built PDF solution that eliminates browser automation overhead while delivering better performance and professional document features.
Before You Start
Prerequisites
- .NET Environment: .NET Framework 4.6.2+ or .NET Core 3.1+ / .NET 5/6/7/8/9+
- NuGet Access: Ability to install NuGet packages
- IronPDF License: Obtain your license key from ironpdf.com
NuGet Package Changes
# Remove Playwright
dotnet remove package Microsoft.Playwright
# Remove browser binaries (reclaim ~400MB disk space)
# Delete the .playwright folder in your project or user directory
# Add IronPDF
dotnet add package IronPdf# Remove Playwright
dotnet remove package Microsoft.Playwright
# Remove browser binaries (reclaim ~400MB disk space)
# Delete the .playwright folder in your project or user directory
# Add IronPDF
dotnet add package IronPdfNo playwright install required with IronPDF - the rendering engine is bundled automatically.
License Configuration
// Add at application startup
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";// Add at application startup
IronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";Complete API Reference
Namespace Changes
// Before: Playwright
using Microsoft.Playwright;
using System.Threading.Tasks;
// After: IronPDF
using IronPdf;
using IronPdf.Rendering;// Before: Playwright
using Microsoft.Playwright;
using System.Threading.Tasks;
// After: IronPDF
using IronPdf;
using IronPdf.Rendering;Core API Mappings
| Playwright API | IronPDF API | Notes |
|---|---|---|
Playwright.CreateAsync() | new ChromePdfRenderer() | No async needed |
playwright.Chromium.LaunchAsync() | Not needed | No browser management |
browser.NewPageAsync() | Not needed | No page context |
page.GotoAsync(url) | renderer.RenderUrlAsPdf(url) | Direct URL rendering |
page.SetContentAsync(html) + page.PdfAsync() | renderer.RenderHtmlAsPdf(html) | Single method |
page.CloseAsync() | Not needed | Automatic cleanup |
browser.CloseAsync() | Not needed | Automatic cleanup |
PagePdfOptions.Format | RenderingOptions.PaperSize | Paper size |
PagePdfOptions.Margin | RenderingOptions.MarginTop/Bottom/Left/Right | Individual margins |
PagePdfOptions.DisplayHeaderFooter | RenderingOptions.TextHeader/TextFooter | Headers/footers |
PagePdfOptions.HeaderTemplate | RenderingOptions.HtmlHeader | HTML headers |
PagePdfOptions.FooterTemplate | RenderingOptions.HtmlFooter | HTML footers |
<span class='pageNumber'> | {page} | Page number placeholder |
Code Migration Examples
Example 1: HTML String to PDF Conversion
Before (Playwright):
// NuGet: Install-Package Microsoft.Playwright
using Microsoft.Playwright;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();
string html = "<h1>Hello World</h1><p>This is a test PDF.</p>";
await page.SetContentAsync(html);
await page.PdfAsync(new PagePdfOptions { Path = "output.pdf" });
await browser.CloseAsync();
}
}// NuGet: Install-Package Microsoft.Playwright
using Microsoft.Playwright;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();
string html = "<h1>Hello World</h1><p>This is a test PDF.</p>";
await page.SetContentAsync(html);
await page.PdfAsync(new PagePdfOptions { Path = "output.pdf" });
await browser.CloseAsync();
}
}After (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
string html = "<h1>Hello World</h1><p>This is a test PDF.</p>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
}
}// NuGet: Install-Package IronPdf
using IronPdf;
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
string html = "<h1>Hello World</h1><p>This is a test PDF.</p>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");
}
}This example demonstrates the fundamental architectural difference. Playwright requires five async operations: Playwright.CreateAsync(), Chromium.LaunchAsync(), NewPageAsync(), SetContentAsync(), and PdfAsync(), plus explicit browser cleanup with CloseAsync().
IronPDF eliminates all this complexity: create a ChromePdfRenderer, call RenderHtmlAsPdf(), and SaveAs(). No async patterns, no browser lifecycle, no cleanup code. IronPDF's approach offers cleaner syntax and better integration with modern .NET applications. See the HTML to PDF documentation for comprehensive examples.
Example 2: URL to PDF Conversion
Before (Playwright):
// NuGet: Install-Package Microsoft.Playwright
using Microsoft.Playwright;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();
await page.GotoAsync("https://www.example.com");
await page.PdfAsync(new PagePdfOptions
{
Path = "webpage.pdf",
Format = "A4"
});
await browser.CloseAsync();
}
}// NuGet: Install-Package Microsoft.Playwright
using Microsoft.Playwright;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();
await page.GotoAsync("https://www.example.com");
await page.PdfAsync(new PagePdfOptions
{
Path = "webpage.pdf",
Format = "A4"
});
await browser.CloseAsync();
}
}After (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
pdf.SaveAs("webpage.pdf");
}
}// NuGet: Install-Package IronPdf
using IronPdf;
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderUrlAsPdf("https://www.example.com");
pdf.SaveAs("webpage.pdf");
}
}Playwright uses GotoAsync() to navigate to a URL followed by PdfAsync(). IronPDF provides a single RenderUrlAsPdf() method that handles navigation and PDF generation in one call. Note that Playwright requires specifying the Format in PagePdfOptions, while IronPDF uses RenderingOptions.PaperSize for paper size configuration. Learn more in our tutorials.
Example 3: Custom Page Size with Margins
Before (Playwright):
// NuGet: Install-Package Microsoft.Playwright
using Microsoft.Playwright;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();
await page.SetContentAsync("<h1>Custom PDF</h1><p>Letter size with margins</p>");
await page.PdfAsync(new PagePdfOptions
{
Path = "custom.pdf",
Format = "Letter",
Margin = new Margin { Top = "1in", Bottom = "1in", Left = "0.5in", Right = "0.5in" }
});
}
}// NuGet: Install-Package Microsoft.Playwright
using Microsoft.Playwright;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();
await page.SetContentAsync("<h1>Custom PDF</h1><p>Letter size with margins</p>");
await page.PdfAsync(new PagePdfOptions
{
Path = "custom.pdf",
Format = "Letter",
Margin = new Margin { Top = "1in", Bottom = "1in", Left = "0.5in", Right = "0.5in" }
});
}
}After (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = PdfPaperSize.Letter;
renderer.RenderingOptions.MarginTop = 25;
renderer.RenderingOptions.MarginBottom = 25;
renderer.RenderingOptions.MarginLeft = 12;
renderer.RenderingOptions.MarginRight = 12;
var pdf = renderer.RenderHtmlAsPdf("<h1>Custom PDF</h1><p>Letter size with margins</p>");
pdf.SaveAs("custom.pdf");
}
}// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
class Program
{
static void Main()
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.PaperSize = PdfPaperSize.Letter;
renderer.RenderingOptions.MarginTop = 25;
renderer.RenderingOptions.MarginBottom = 25;
renderer.RenderingOptions.MarginLeft = 12;
renderer.RenderingOptions.MarginRight = 12;
var pdf = renderer.RenderHtmlAsPdf("<h1>Custom PDF</h1><p>Letter size with margins</p>");
pdf.SaveAs("custom.pdf");
}
}Playwright uses string-based margin values ("1in", "0.5in") while IronPDF uses numeric millimeter values. The conversion is: 1 inch = 25.4mm, so "1in" becomes 25 and "0.5in" becomes approximately 12. Playwright's Format = "Letter" maps to IronPDF's PaperSize = PdfPaperSize.Letter.
Example 4: Headers, Footers, and Custom Settings
Before (Playwright):
// NuGet: Install-Package Microsoft.Playwright
using Microsoft.Playwright;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();
string html = "<h1>Custom PDF</h1><p>With margins and headers.</p>";
await page.SetContentAsync(html);
await page.PdfAsync(new PagePdfOptions
{
Path = "custom.pdf",
Format = "A4",
Margin = new Margin { Top = "1cm", Bottom = "1cm", Left = "1cm", Right = "1cm" },
DisplayHeaderFooter = true,
HeaderTemplate = "<div style='font-size:10px; text-align:center;'>Header</div>",
FooterTemplate = "<div style='font-size:10px; text-align:center;'>Page <span class='pageNumber'></span></div>"
});
await browser.CloseAsync();
}
}// NuGet: Install-Package Microsoft.Playwright
using Microsoft.Playwright;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
var playwright = await Playwright.CreateAsync();
var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();
string html = "<h1>Custom PDF</h1><p>With margins and headers.</p>";
await page.SetContentAsync(html);
await page.PdfAsync(new PagePdfOptions
{
Path = "custom.pdf",
Format = "A4",
Margin = new Margin { Top = "1cm", Bottom = "1cm", Left = "1cm", Right = "1cm" },
DisplayHeaderFooter = true,
HeaderTemplate = "<div style='font-size:10px; text-align:center;'>Header</div>",
FooterTemplate = "<div style='font-size:10px; text-align:center;'>Page <span class='pageNumber'></span></div>"
});
await browser.CloseAsync();
}
}After (IronPDF):
// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.MarginTop = 10;
renderer.RenderingOptions.MarginBottom = 10;
renderer.RenderingOptions.MarginLeft = 10;
renderer.RenderingOptions.MarginRight = 10;
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;
renderer.RenderingOptions.TextHeader.CenterText = "Header";
renderer.RenderingOptions.TextFooter.CenterText = "Page {page}";
string html = "<h1>Custom PDF</h1><p>With margins and headers.</p>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("custom.pdf");
}
}// NuGet: Install-Package IronPdf
using IronPdf;
using IronPdf.Rendering;
class Program
{
static void Main(string[] args)
{
var renderer = new ChromePdfRenderer();
renderer.RenderingOptions.MarginTop = 10;
renderer.RenderingOptions.MarginBottom = 10;
renderer.RenderingOptions.MarginLeft = 10;
renderer.RenderingOptions.MarginRight = 10;
renderer.RenderingOptions.PaperSize = PdfPaperSize.A4;
renderer.RenderingOptions.TextHeader.CenterText = "Header";
renderer.RenderingOptions.TextFooter.CenterText = "Page {page}";
string html = "<h1>Custom PDF</h1><p>With margins and headers.</p>";
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("custom.pdf");
}
}This example shows the header/footer placeholder syntax difference. Playwright uses HTML class-based placeholders (<span class='pageNumber'></span>), while IronPDF uses curly brace placeholders ({page}). Note that Playwright requires DisplayHeaderFooter = true to enable headers/footers, while IronPDF enables them automatically when you set header/footer content.
Critical Migration Notes
Async to Sync Conversion
Playwright requires async/await throughout; IronPDF supports synchronous operations:
// Playwright: Async required
public async Task<byte[]> GeneratePdfAsync(string html)
{
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();
await page.SetContentAsync(html);
return await page.PdfAsync();
}
// IronPDF: Sync is simpler
public byte[] GeneratePdf(string html)
{
var renderer = new ChromePdfRenderer();
return renderer.RenderHtmlAsPdf(html).BinaryData;
}// Playwright: Async required
public async Task<byte[]> GeneratePdfAsync(string html)
{
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();
await page.SetContentAsync(html);
return await page.PdfAsync();
}
// IronPDF: Sync is simpler
public byte[] GeneratePdf(string html)
{
var renderer = new ChromePdfRenderer();
return renderer.RenderHtmlAsPdf(html).BinaryData;
}Margin Unit Conversion
Playwright uses string units; IronPDF uses numeric millimeters:
| Playwright | IronPDF (mm) |
|---|---|
"1in" | 25 |
"0.5in" | 12 |
"1cm" | 10 |
Header/Footer Placeholder Conversion
| Playwright Class | IronPDF Placeholder |
|---|---|
<span class='pageNumber'> | {page} |
<span class='totalPages'> | {total-pages} |
<span class='date'> | {date} |
<span class='title'> | {html-title} |
Browser Lifecycle Elimination
Remove all browser management code:
// Playwright: Explicit cleanup required
await page.CloseAsync();
await browser.CloseAsync();
playwright.Dispose();
// IronPDF: No disposal needed - just use the renderer
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");// Playwright: Explicit cleanup required
await page.CloseAsync();
await browser.CloseAsync();
playwright.Dispose();
// IronPDF: No disposal needed - just use the renderer
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(html);
pdf.SaveAs("output.pdf");New Capabilities After Migration
After migrating to IronPDF, you gain capabilities that Playwright cannot provide:
PDF Merging
var pdf1 = renderer.RenderHtmlAsPdf(html1);
var pdf2 = renderer.RenderHtmlAsPdf(html2);
var merged = PdfDocument.Merge(pdf1, pdf2);
merged.SaveAs("merged.pdf");var pdf1 = renderer.RenderHtmlAsPdf(html1);
var pdf2 = renderer.RenderHtmlAsPdf(html2);
var merged = PdfDocument.Merge(pdf1, pdf2);
merged.SaveAs("merged.pdf");Watermarks
pdf.ApplyWatermark("<h1 style='color:red; opacity:0.3;'>DRAFT</h1>");pdf.ApplyWatermark("<h1 style='color:red; opacity:0.3;'>DRAFT</h1>");Password Protection
pdf.SecuritySettings.OwnerPassword = "admin";
pdf.SecuritySettings.UserPassword = "readonly";
pdf.SecuritySettings.AllowUserCopyPasteContent = false;pdf.SecuritySettings.OwnerPassword = "admin";
pdf.SecuritySettings.UserPassword = "readonly";
pdf.SecuritySettings.AllowUserCopyPasteContent = false;Digital Signatures
var signature = new PdfSignature("certificate.pfx", "password");
pdf.Sign(signature);var signature = new PdfSignature("certificate.pfx", "password");
pdf.Sign(signature);PDF/A Compliance
pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b);pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b);Migration Checklist
Pre-Migration
- Identify all Playwright PDF generation code
- Document margin values (convert inches/cm to millimeters)
- Note header/footer placeholder syntax for conversion
- Obtain IronPDF license key from ironpdf.com
Package Changes
- Remove
Microsoft.PlaywrightNuGet package - Delete
.playwrightfolder to reclaim ~400MB disk space - Install
IronPdfNuGet package:dotnet add package IronPdf
Code Changes
- Update namespace imports
- Replace async browser lifecycle with
ChromePdfRenderer - Convert
page.SetContentAsync()+page.PdfAsync()toRenderHtmlAsPdf() - Convert
page.GotoAsync()+page.PdfAsync()toRenderUrlAsPdf() - Convert margin strings to millimeter values
- Convert header/footer placeholder syntax
- Remove all browser/page disposal code
- Add license initialization at application startup
Post-Migration
- Visual comparison of PDF output
- Verify header/footer rendering with page numbers
- Test margin and page sizing accuracy
- Add new capabilities (security, watermarks, merging) as needed






