ScreenshotNeo

BlogComparisons

HTML to PDF Libraries for C#

Compare the best C# HTML-to-PDF libraries, with runnable examples, licensing guidance, browser trade-offs, and production troubleshooting.

By the ScreenshotNeo team1 October 20268 min read

Short answer: For HTML that depends on modern CSS, web fonts, images, or JavaScript, begin with a Chromium-based renderer. In C#, the practical choices are IronPDF, PuppeteerSharp, or Playwright .NET. Choose iText pdfHTML when you need iText’s parser-based PDF workflow, PDF manipulation, or its documented PDF/UA path. Do not select PDFsharp as a standalone HTML renderer: it creates and edits PDFs but does not render HTML.

Your choice depends on five things: rendering fidelity, browser deployment, concurrency and memory, pagination controls, and licensing. There is no neutral benchmark in the available research, so measure your own templates, assets, fonts, page counts, and concurrency before committing.

How the main C# options compare

Library Rendering approach JavaScript Best fit Licensing or deployment notes
IronPDF Chromium-based renderer Yes High-level commercial component with browser-like output Commercial product; package and runtime requirements should be reviewed for deployment
PuppeteerSharp Controls Chrome or Chromium Yes Teams wanting MIT licensing and direct browser control MIT package; you must package, update, and operate the browser runtime
iText pdfHTML Parser-based HTML/CSS conversion Not a browser JavaScript runtime Projects already using iText, PDF manipulation, or PDF/UA workflows AGPL or commercial licensing applies; closed-source commercial use requires a commercial license
Playwright .NET Automates Chromium, Firefox, and WebKit Yes Applications that already use Playwright for browser automation Verify the exact release’s PDF API and browser deployment model
wkhtmltopdf WebKit executable or wrapper Limited compared with current browsers Legacy systems that already depend on its output Requires a native executable or wrapper and operational management
PDFsharp PDF creation and editing No HTML renderer Drawing or editing PDF content after another renderer has produced it Pair it with a separate HTML renderer

IronPDF: high-level Chromium rendering

IronPDF’s official C# tutorial shows NuGet installation, conversion from HTML strings and URLs, conversion of HTML pages, custom headers and footers, and SaveAs. Its Chromium-based renderer is intended to match Google Chrome output.

dotnet add package IronPdf
using IronPdf;

var renderer = new ChromePdfRenderer();

// From an HTML string
var document = renderer.RenderHtmlAsPdf("""
<!doctype html>
<html>
<head>
  <meta charset='utf-8'>
  <style>body { font-family: Arial, sans-serif; }</style>
</head>
<body><h1>Invoice</h1><p>Amount due: $42</p></body>
</html>
""");
document.SaveAs("invoice.pdf");

// From a URL
var fromUrl = renderer.RenderUrlAsPdf("https://example.com");
fromUrl.SaveAs("example.pdf");

Use this route when a supported commercial component and a concise C# API justify the license cost. Confirm how the package obtains its Chromium runtime in your target environment, then test fonts, images, JavaScript, and sandbox restrictions in the same container or host model you will run in production.

PuppeteerSharp: direct Chromium control

PuppeteerSharp is a .NET port of the official Node.js Puppeteer API. Its documented flow launches Chromium, creates a page, navigates to a URL or sets HTML content, waits for a selector when necessary, and calls PdfAsync. The NuGet package is MIT licensed.

dotnet add package PuppeteerSharp
using PuppeteerSharp;

await new BrowserFetcher().DownloadAsync();
await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
    Headless = true,
    Args = new[] { "--no-sandbox", "--disable-setuid-sandbox" }
});

await using var page = await browser.NewPageAsync();
await page.SetViewportAsync(new ViewPortOptions { Width = 1280, Height = 900, DeviceScaleFactor = 1 });
await page.GoToAsync("https://example.com", WaitUntilNavigation.Networkidle0);
await page.WaitForSelectorAsync("main");
await page.PdfAsync("example.pdf", new PdfOptions
{
    Format = PaperFormat.A4,
    PrintBackground = true,
    PreferCSSPageSize = true,
    MarginOptions = new MarginOptions
    {
        Top = "16mm", Right = "16mm", Bottom = "16mm", Left = "16mm"
    }
});

For HTML generated by your application, replace navigation with SetContentAsync and wait for the application-specific ready selector. Keep browser launch outside the per-request path when possible, but isolate pages and clear state between jobs.

iText pdfHTML: parser-based conversion and PDF workflows

iText documents pdfHTML as an add-on for converting HTML/XML and CSS to PDF in Java and C#. Its feature matrix includes HTML/CSS conversion and an HTML-to-PDF/UA path. It is a strong fit when the project already uses iText or needs substantial PDF post-processing.

dotnet add package itext7
 dotnet add package itext7.pdfhtml
using iText.Html2pdf;
using iText.Kernel.Pdf;

using var writer = new PdfWriter("report.pdf");
using var pdf = new PdfDocument(writer);
HtmlConverter.ConvertToPdf("<h1>Report</h1><p>Generated from HTML.</p>", pdf);

Review licensing before shipping. iText’s guidance says commercial or closed-source use requires a commercial license and the appropriate license-key library; AGPL obligations may apply to other deployments. Involve your legal team for the exact distribution model.

Playwright .NET: use the browser automation stack you already have

Playwright .NET is Microsoft’s official .NET port for automating Chromium, Firefox, and WebKit through one API. It can be appropriate when your application already provisions Playwright browsers and uses the same fixtures for testing or automation. Verify the exact version’s PDF-printing API and deployment instructions before presenting it as a dedicated converter.

dotnet add package Microsoft.Playwright
# Install the browsers as described by the package for your release.
using Microsoft.Playwright;

using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
    Headless = true
});
var page = await browser.NewPageAsync();
await page.GotoAsync("https://example.com", new PageGotoOptions
{
    WaitUntil = WaitUntilState.NetworkIdle
});
await page.PdfAsync(new PagePdfOptions
{
    Path = "example.pdf",
    Format = "A4",
    PrintBackground = true,
    PreferCSSPageSize = true
});

Keep browser versions pinned and validate output after upgrades. Browser automation packages can have different runtime, sandbox, and container requirements from one release to the next.

wkhtmltopdf and PDFsharp: where they fit

wkhtmltopdf

wkhtmltopdf integrations invoke a native executable or wrapper. This can be workable for a legacy system whose existing PDFs are already accepted, but it adds executable discovery, process management, patching, and platform-specific deployment work. Treat its WebKit rendering behavior as distinct from current Chromium behavior and test modern CSS and JavaScript pages explicitly.

PDFsharp

PDFsharp is a PDF creation and editing component, not an HTML-to-PDF renderer. Use it after a separate renderer when you need to merge, draw, annotate, or otherwise manipulate PDF objects.

How to choose

  1. Need current CSS and JavaScript fidelity? Start with IronPDF, PuppeteerSharp, or Playwright .NET.
  2. Need MIT licensing and low-level Chromium control? Evaluate PuppeteerSharp, including the cost of packaging and updating Chromium.
  3. Need iText PDF features or a documented PDF/UA route? Evaluate iText pdfHTML and resolve AGPL versus commercial licensing first.
  4. Already run Playwright? Reuse Playwright .NET if its PDF API and browser deployment fit your version.
  5. Need PDF editing rather than HTML rendering? Pair PDFsharp with a renderer.
  6. Have a legacy wkhtmltopdf output contract? Keep it only after testing the pages that matter and documenting native runtime operations.

Production checklist: fidelity, reliability, and cost

  • Pin the library and browser/runtime versions; upgrade them as a tested unit.
  • Load the same fonts, images, and JavaScript dependencies in staging and production.
  • Define a readiness signal such as a selector or application flag instead of relying only on a fixed delay.
  • Set explicit navigation, resource, and PDF timeouts. Record the URL, template version, renderer version, and failure reason.
  • Use separate browser contexts or pages per job so cookies, local storage, and authentication do not leak between customers.
  • Limit concurrency according to measured CPU and memory use. Browser processes can consume substantially more resources than parser-only conversion.
  • Cache stable assets and avoid downloading unnecessary trackers or analytics during rendering.
  • Specify print CSS, page size, margins, background printing, and page-break rules in the template.
  • Test long tables, very tall images, missing fonts, right-to-left text, SVG, video, lazy-loaded images, and pages that change after load.
  • Measure your own workload. The reviewed sources publish no neutral decision-grade speed, memory, accuracy, or market-share benchmark.

Common errors and fixes

Symptom Likely cause Fix
Browser executable not found Chromium or another browser was not downloaded or is outside the configured path Install the browser for the package version, configure its executable path, and verify the same path inside the production container.
Blank or incomplete PDF Conversion started before client rendering or fonts finished loading Wait for a specific selector or application-ready signal; then check console errors and failed network requests.
Images missing Relative URLs, blocked requests, authentication, or a page that lazy-loads below the viewport Use absolute asset URLs, provide required headers or cookies, scroll or trigger lazy loading, and inspect request logs.
CSS looks different from the browser Print media rules, unsupported CSS, missing fonts, or different viewport dimensions Set the viewport and print options explicitly, include print CSS, install fonts, and compare the same browser version.
JavaScript content is absent Navigation completed before the application rendered Wait for a stable selector or network-idle condition, with a bounded timeout.
Pages break in the wrong places Missing print rules or conflicting fixed-height containers Use @page, break-inside, break-before, and flexible heights; test tables and repeated headers.
Process crashes under load Too many concurrent browser pages or leaked contexts Bound concurrency, close pages and contexts in finally blocks, and measure memory per job.
License or deployment review blocks release AGPL/commercial terms or native browser requirements were not assessed early Choose a license-compatible library, obtain required keys, and document runtime installation in deployment automation.

Or skip the browser setup

If your goal is a clean PDF or screenshot from a URL rather than operating a browser inside your C# service, ScreenshotNeo provides a website screenshot API and MCP server. Its PDF endpoint accepts one GET request, and the API documentation lists the options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Cookie and consent banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers identify the page verdict and billing status. An MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.

FAQ

Which library supports the most modern web pages?

A Chromium-based renderer is the safest starting point for JavaScript-heavy pages and current CSS. Compare IronPDF, PuppeteerSharp, and Playwright .NET against your own templates.

Is PuppeteerSharp free for commercial software?

The PuppeteerSharp NuGet package is MIT licensed. You still need to account for the browser runtime and your application’s other dependencies.

Does iText pdfHTML execute JavaScript?

pdfHTML is a parser-based HTML and CSS converter, not a full browser JavaScript runtime. Use a browser-based renderer when client-side execution is required.

Can PDFsharp convert an HTML page?

No. PDFsharp needs a separate HTML renderer; it is intended for creating and editing PDF content.

How should I benchmark candidates?

Render representative pages with your real fonts, images, scripts, page counts, and concurrency. Compare visual output, failure behavior, resource use, and total licensing and runtime cost.