Photo from Pexels
Originally Posted On: https://ironpdf.com/blog/migration-guides/migrate-from-puppeteersharp-to-ironpdf/
Migrating from PuppeteerSharp to IronPDF transforms your PDF generation workflow from a browser automation tool with 300MB+ dependencies to a purpose-built PDF library with automatic memory management. This guide provides a complete, step-by-step migration path that eliminates Chromium downloads, solves memory leak issues, and provides comprehensive PDF manipulation capabilities.
Why Migrate from PuppeteerSharp to IronPDF
Understanding PuppeteerSharp
PuppeteerSharp is a .NET port of Google’s Puppeteer, bringing browser automation capabilities to C#. It generates PDFs using Chrome’s built-in print-to-PDF functionality—the same as hitting Ctrl+P in a browser. This produces print-ready output optimized for paper, which differs from what you see on screen.
PuppeteerSharp was designed for web testing and scraping, not document generation. While capable, using PuppeteerSharp for PDF generation creates significant production challenges.
The Browser Automation Problem
PuppeteerSharp was designed for browser automation, not document generation. This creates fundamental issues when using it for PDFs:
- 300MB+ Chromium downloads required before first use. A significant downside of PuppeteerSharp is its hefty deployment size, mainly due to the Chromium binary it bundles. This substantial size can bloat Docker images and cause cold start issues in serverless environments.
- Memory leaks under load requiring manual browser recycling. Under heavy load, PuppeteerSharp is known to experience memory leaks. The accumulation of memory by browser instances necessitates manual intervention for process management and recycling.
- Complex async patterns with browser lifecycle management.
- Print-to-PDF output (equivalent to Ctrl+P, not screen capture). Layouts may reflow, backgrounds may be omitted by default, and the output is paginated for printing rather than matching the browser viewport.
- No PDF/A or PDF/UA support for compliance requirements. PuppeteerSharp cannot produce PDF/A (archival) or PDF/UA (accessibility) compliant documents.
- No PDF manipulation – generation only, no merge/split/edit. While PuppeteerSharp is efficient at generating PDFs, it lacks capabilities for further manipulation such as merging, splitting, securing, or editing PDFs.
PuppeteerSharp vs IronPDF Comparison
| Aspect | PuppeteerSharp | IronPDF |
|---|---|---|
| Primary Purpose | Browser automation | PDF generation |
| Chromium Dependency | 300MB+ separate download | Built-in optimized engine |
| API Complexity | Async browser/page lifecycle | Synchronous one-liners |
| Initialization | BrowserFetcher.DownloadAsync() + LaunchAsync |
new ChromePdfRenderer() |
| Memory Management | Manual browser recycling required | Automatic |
| Memory Under Load | 500MB+ with leaks | ~50MB stable |
| Cold Start | 45+ seconds | ~20 seconds |
| PDF/A Support | Not available | Supported |
| PDF/UA Accessibility | Not available | Supported |
| PDF Editing | Not available | Merge, split, stamp, edit |
| Digital Signatures | Not available | Supported |
| Thread Safety | Limited | Full |
| Professional Support | Community | Commercial with SLA |
Platform Support
| Library | .NET Framework 4.7.2 | .NET Core 3.1 | .NET 6-8 | .NET 10 | | ——— | :—: | :—: | :—: | :—: || IronPDF | Full | Full | Full | Full || PuppeteerSharp | Limited | Full | Full | Pending | IronPDF’s extensive support across .NET platforms ensures developers can leverage it in various environments without encountering compatibility issues, providing a flexible choice for modern .NET applications through 2025 and 2026.
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 PuppeteerSharpdotnet remove package PuppeteerSharp# Remove downloaded Chromium binaries (~300MB recovered)# Delete the .local-chromium folder# Add IronPDFdotnet add package IronPdf
No BrowserFetcher.DownloadAsync() required with IronPDF – the rendering engine is bundled automatically.
License Configuration
// Add at application startupIronPdf.License.LicenseKey = "YOUR-LICENSE-KEY";
Complete API Reference
Namespace Changes
// Before: PuppeteerSharpusing PuppeteerSharp;using PuppeteerSharp.Media;using System.Threading.Tasks;// After: IronPDFusing IronPdf;using IronPdf.Rendering;
Core API Mappings
| PuppeteerSharp API | IronPDF API | Notes |
|---|---|---|
new BrowserFetcher().DownloadAsync() |
Not needed | No browser download |
Puppeteer.LaunchAsync(options) |
Not needed | No browser management |
browser.NewPageAsync() |
Not needed | No page context |
page.GoToAsync(url) |
renderer.RenderUrlAsPdf(url) |
Direct rendering |
page.SetContentAsync(html) |
renderer.RenderHtmlAsPdf(html) |
Direct rendering |
page.PdfAsync(path) |
pdf.SaveAs(path) |
After rendering |
await page.CloseAsync() |
Not needed | Automatic cleanup |
await browser.CloseAsync() |
Not needed | Automatic cleanup |
PdfOptions.Format |
RenderingOptions.PaperSize |
Paper size |
PdfOptions.Landscape |
RenderingOptions.PaperOrientation |
Orientation |
PdfOptions.MarginOptions |
RenderingOptions.MarginTop/Bottom/Left/Right |
Individual margins |
PdfOptions.PrintBackground |
RenderingOptions.PrintHtmlBackgrounds |
Background printing |
PdfOptions.HeaderTemplate |
RenderingOptions.HtmlHeader |
HTML headers |
PdfOptions.FooterTemplate |
RenderingOptions.HtmlFooter |
HTML footers |
page.WaitForSelectorAsync() |
RenderingOptions.WaitFor.HtmlElementId |
Wait for element |
Code Migration Examples
Example 1: Basic HTML to PDF Conversion
Before (PuppeteerSharp):
// NuGet: Install-Package PuppeteerSharpusing PuppeteerSharp;using System.Threading.Tasks;class Program{static async Task Main(string[] args){var browserFetcher = new BrowserFetcher();await browserFetcher.DownloadAsync();await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions{Headless = true});await using var page = await browser.NewPageAsync();await page.SetContentAsync("<h1>Hello World</h1><p>This is a PDF document.</p>");await page.PdfAsync("output.pdf");}}
After (IronPDF):
// NuGet: Install-Package IronPdfusing IronPdf;class Program{static void Main(string[] args){var renderer = new ChromePdfRenderer();var pdf = renderer.RenderHtmlAsPdf("<h1>Hello World</h1><p>This is a PDF document.</p>");pdf.SaveAs("output.pdf");}}
This example demonstrates the fundamental architectural difference. PuppeteerSharp requires six async operations: BrowserFetcher.DownloadAsync() (300MB+ Chromium download), Puppeteer.LaunchAsync(), browser.NewPageAsync(), page.SetContentAsync(), and page.PdfAsync(), plus proper disposal with await using.
IronPDF eliminates all this complexity: create a ChromePdfRenderer, call RenderHtmlAsPdf(), and SaveAs(). No async patterns, no browser lifecycle, no Chromium downloads. 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 (PuppeteerSharp):
// NuGet: Install-Package PuppeteerSharpusing PuppeteerSharp;using System.Threading.Tasks;class Program{static async Task Main(string[] args){var browserFetcher = new BrowserFetcher();await browserFetcher.DownloadAsync();await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions{Headless = true});await using var page = await browser.NewPageAsync();await page.GoToAsync("https://www.example.com");await page.PdfAsync("webpage.pdf");}}
After (IronPDF):
// NuGet: Install-Package IronPdfusing IronPdf;class Program{static void Main(string[] args){var renderer = new ChromePdfRenderer();var pdf = renderer.RenderUrlAsPdf("https://www.example.com");pdf.SaveAs("webpage.pdf");}}
PuppeteerSharp 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. Learn more in our tutorials.
Example 3: Custom Page Settings with Margins
Before (PuppeteerSharp):
// NuGet: Install-Package PuppeteerSharpusing PuppeteerSharp;using PuppeteerSharp.Media;using System.Threading.Tasks;class Program{static async Task Main(string[] args){var browserFetcher = new BrowserFetcher();await browserFetcher.DownloadAsync();await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions{Headless = true});await using var page = await browser.NewPageAsync();await page.SetContentAsync("<h1>Custom PDF</h1><p>With landscape orientation and margins.</p>");await page.PdfAsync("custom.pdf", new PdfOptions{Format = PaperFormat.A4,Landscape = true,MarginOptions = new MarginOptions{Top = "20mm",Bottom = "20mm",Left = "20mm",Right = "20mm"}});}}
After (IronPDF):
// NuGet: Install-Package IronPdfusing IronPdf;using IronPdf.Rendering;class Program{static void Main(string[] args){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 = 20;renderer.RenderingOptions.MarginRight = 20;var pdf = renderer.RenderHtmlAsPdf("<h1>Custom PDF</h1><p>With landscape orientation and margins.</p>");pdf.SaveAs("custom.pdf");}}
This example shows how PDF options map between the two libraries. PuppeteerSharp uses PdfOptions with Format, Landscape, and MarginOptions containing string values ("20mm"). IronPDF uses RenderingOptions properties with direct paper size enums, orientation enums, and numeric margin values in millimeters.
Key mappings:
Format = PaperFormat.A4→PaperSize = PdfPaperSize.A4Landscape = true→PaperOrientation = PdfPaperOrientation.LandscapeMarginOptions.Top = "20mm"→MarginTop = 20(numeric millimeters)
The Memory Leak Problem
PuppeteerSharp is notorious for memory accumulation under sustained load:
// ❌ PuppeteerSharp - Memory grows with each operation// Requires explicit browser recycling every N operationsfor (int i = 0; i < 1000; i++){var page = await browser.NewPageAsync();await page.SetContentAsync($"<h1>Document {i}</h1>");await page.PdfAsync($"doc_{i}.pdf");await page.CloseAsync(); // Memory still accumulates!}// Must periodically: await browser.CloseAsync(); and re-launch// ✅ IronPDF - Stable memory, reuse renderervar renderer = new ChromePdfRenderer();for (int i = 0; i < 1000; i++){var pdf = renderer.RenderHtmlAsPdf($"<h1>Document {i}</h1>");pdf.SaveAs($"doc_{i}.pdf");// Memory managed automatically}
IronPDF eliminates the need for browser pooling infrastructure that PuppeteerSharp requires:
// Before (PuppeteerSharp - delete entire class)public class PuppeteerBrowserPool{private readonly ConcurrentBag<IBrowser> _browsers;private readonly SemaphoreSlim _semaphore;private int _operationCount;// ... recycling logic ...}// After (IronPDF - simple reuse)public class PdfService{private readonly ChromePdfRenderer _renderer = new();public byte[] Generate(string html){return _renderer.RenderHtmlAsPdf(html).BinaryData;}}
Critical Migration Notes
Async to Sync Conversion
PuppeteerSharp requires async/await throughout; IronPDF supports synchronous operations:
// PuppeteerSharp: Async requiredpublic async Task<byte[]> GeneratePdfAsync(string html){await new BrowserFetcher().DownloadAsync();await using var browser = await Puppeteer.LaunchAsync(...);await using var page = await browser.NewPageAsync();await page.SetContentAsync(html);return await page.PdfDataAsync();}// IronPDF: Sync defaultpublic byte[] GeneratePdf(string html){var renderer = new ChromePdfRenderer();return renderer.RenderHtmlAsPdf(html).BinaryData;}// Or async when neededpublic async Task<byte[]> GeneratePdfAsync(string html){var renderer = new ChromePdfRenderer();var pdf = await renderer.RenderHtmlAsPdfAsync(html);return pdf.BinaryData;}
Margin Unit Conversion
PuppeteerSharp uses string units; IronPDF uses numeric millimeters:
// PuppeteerSharp - string unitsMarginOptions = new MarginOptions{Top = "1in", // 25.4mmBottom = "0.75in", // 19mmLeft = "1cm", // 10mmRight = "20px" // ~7.5mm at 96dpi}// IronPDF - numeric millimetersrenderer.RenderingOptions.MarginTop = 25; // mmrenderer.RenderingOptions.MarginBottom = 19;renderer.RenderingOptions.MarginLeft = 10;renderer.RenderingOptions.MarginRight = 8;
Header/Footer Placeholder Conversion
| PuppeteerSharp Class | IronPDF Placeholder |
|---|---|
<span class='pageNumber'> |
{page} |
<span class='totalPages'> |
{total-pages} |
<span class='date'> |
{date} |
<span class='title'> |
{html-title} |
New Capabilities After Migration
After migrating to IronPDF, you gain capabilities that PuppeteerSharp cannot provide:
PDF Merging
var pdf1 = renderer.RenderHtmlAsPdf(html1);var pdf2 = renderer.RenderHtmlAsPdf(html2);var merged = PdfDocument.Merge(pdf1, pdf2);merged.SaveAs("merged.pdf");
Watermarks
var watermark = new TextStamper{Text = "CONFIDENTIAL",FontSize = 48,Opacity = 30,Rotation = -45};pdf.ApplyStamp(watermark);
Password Protection
pdf.SecuritySettings.OwnerPassword = "admin";pdf.SecuritySettings.UserPassword = "readonly";pdf.SecuritySettings.AllowUserCopyPasteContent = false;
Digital Signatures
var signature = new PdfSignature("certificate.pfx", "password");pdf.Sign(signature);
PDF/A Compliance
pdf.SaveAsPdfA("archive.pdf", PdfAVersions.PdfA3b);
Performance Comparison Summary
| Metric | PuppeteerSharp | IronPDF | Improvement |
|---|---|---|---|
| First PDF (Cold Start) | 45s+ | ~20s | 55%+ faster |
| Subsequent PDFs | Variable | Consistent | Predictable |
| Memory Usage | 500MB+ (grows) | ~50MB (stable) | 90% less memory |
| Disk Space (Chromium) | 300MB+ | 0 | Eliminate downloads |
| Browser Download | Required | Not needed | Zero setup |
| Thread Safety | Limited | Full | Reliable concurrency |
| PDF Generation Time | 45s | 20s | 55% faster |
Migration Checklist
Pre-Migration
- Identify all PuppeteerSharp usages in codebase
- Document margin values (convert strings to millimeters)
- Note header/footer placeholder syntax for conversion
- Delete browser pooling/recycling infrastructure
- Obtain IronPDF license key from ironpdf.com
Package Changes
- Remove
PuppeteerSharpNuGet package - Delete
.local-chromiumfolder to reclaim ~300MB disk space - Install
IronPdfNuGet package:dotnet add package IronPdf
Code Changes
- Update namespace imports
- Remove
BrowserFetcher.DownloadAsync()calls - Remove
Puppeteer.LaunchAsync()and browser management - Replace
page.SetContentAsync()+page.PdfAsync()withRenderHtmlAsPdf() - Replace
page.GoToAsync()+page.PdfAsync()withRenderUrlAsPdf() - Convert margin strings to millimeter values
- Convert header/footer placeholder syntax
- Remove all browser/page disposal code
- Delete browser pooling infrastructure
- Add license initialization at application startup
Post-Migration
- Visual comparison of PDF output
- Load test for memory stability (should stay stable without recycling)
- Verify header/footer rendering with page numbers
- Add new capabilities (security, watermarks, merging) as needed
