October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
.NET

How to Convert HTML to PDF with Microsoft Playwright in C#

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use Microsoft.Playwright’s Page.PdfAsync method. Open the page (or set its HTML), wait until the content is ready, then export it:

await page.PdfAsync(new() { Path = "output.pdf" });

Playwright renders PDFs with print CSS by default. If the PDF must match the screen presentation, call EmulateMediaAsync with Media.Screen before exporting.

What you need before converting HTML

  • A .NET project with the Microsoft.Playwright NuGet package.
  • The browser binary required by the Playwright version installed in that project.
  • A URL or HTML document that your Playwright page can load.
  • A writable destination for the generated PDF.

Playwright browser binaries are version-specific. Microsoft’s documentation states that each Playwright version needs specific browser versions. Install the browsers after adding the package, and repeat that installation after upgrading Playwright. In a build pipeline or Linux host, install the system dependencies documented for your browser as well.

Step-by-step: convert a web page to PDF in C#

1. Create the project and add Playwright

dotnet new console -n HtmlToPdf
cd HtmlToPdf
dotnet add package Microsoft.Playwright

Build the project once, then run the Playwright installation script generated under the build output. For a typical .NET 8 Debug build, the command is:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
dotnet build
pwsh bin/Debug/net8.0/playwright.ps1 install

Use the framework directory produced by your project if it is not net8.0. On CI or Linux, use the corresponding Playwright CLI option to install browser operating-system dependencies when required.

2. Launch a browser, open the page, and export

using Microsoft.Playwright;

using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync(new()
{
    Headless = true
});

var page = await browser.NewPageAsync();
await page.GotoAsync("https://example.com");

await page.PdfAsync(new()
{
    Path = "output.pdf"
});

PdfAsync returns the PDF data and, when Path is supplied, writes the file to that path. The example uses Chromium because PDF generation is provided by the Chromium browser engine.

3. Wait for content that is not ready at navigation time

Navigation completing does not guarantee that client-rendered data, fonts, images, or lazy sections are ready. Choose a wait that matches the page rather than adding an arbitrary long delay.

await page.GotoAsync("https://example.com/report");
await page.WaitForSelectorAsync("#report-ready");
await page.PdfAsync(new() { Path = "report.pdf" });

If the page has no reliable readiness element, wait for a known response or use a short, deliberate timeout for the final asset. Inspect the resulting PDF for clipped content and missing fonts instead of assuming that a navigation event means the page is complete.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Print CSS versus screen CSS

PDF generation uses print media by default. That is usually desirable because a page’s @media print rules can remove navigation, change colors, or control page breaks. To render the screen design instead, select screen media before calling PdfAsync:

await page.EmulateMediaAsync(new()
{
    Media = Media.Screen
});

await page.PdfAsync(new()
{
    Path = "screen-styled.pdf"
});

Do not select screen media merely because the browser preview looks better. Decide whether the deliverable is a printable document or a screen-faithful snapshot, then test that choice against the actual HTML and CSS.

PDF layout options worth setting

The .NET binding exposes PDF options for paper size, explicit dimensions, margins, scaling, page ranges, backgrounds, and CSS page-size precedence. Property names can vary with the binding version, so check the Microsoft.Playwright .NET API for the version in your project before copying a JavaScript example.

Paper, dimensions, and margins

await page.PdfAsync(new()
{
    Path = "letter.pdf",
    Format = "Letter",
    Margin = new()
    {
        Top = "0.5in",
        Right = "0.5in",
        Bottom = "0.5in",
        Left = "0.5in"
    }
});

Letter is the documented default when no format or dimensions are supplied. Use a named format when the output must follow a known paper standard; use width and height when the document has a custom page size.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Let CSS @page control the size

When the document defines its own @page size, enable the option that gives CSS page size priority (named PreferCSSPageSize in the .NET binding). Otherwise, an API format or dimensions setting can override the stylesheet.

await page.PdfAsync(new()
{
    Path = "css-sized.pdf",
    PreferCSSPageSize = true
});

Backgrounds, scale, and page ranges

Background printing is an explicit PDF option. Enable it when branded color blocks or background images are part of the intended document. Scale and page-range options are useful for fitting a dense report or exporting only selected pages.

await page.PdfAsync(new()
{
    Path = "selected-pages.pdf",
    PrintBackground = true,
    Scale = 0.9f,
    PageRanges = "1-3,5"
});

Verify the exact option types for your installed .NET package; some releases represent scale and range values differently.

Headers and footers

Header and footer templates are supported, but their scripts are not evaluated and normal page styles are not visible inside the templates. Put the required styling directly in the template and do not rely on page JavaScript to populate it.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Controlling the HTML before export

Convert a string of HTML

var page = await browser.NewPageAsync();
await page.SetContentAsync("""



  

Invoice

Generated by Playwright.

"""); await page.PdfAsync(new() { Path = "invoice.pdf", PreferCSSPageSize = true });

Make page breaks predictable

Use print CSS to keep headings with following content and to avoid splitting rows where the browser supports it:

@media print {
  h1, h2, h3 { break-after: avoid; }
  table, img { break-inside: avoid; }
}

Large images, web fonts, and lazy-loaded sections are common sources of differences. Wait for a known ready state, and inspect a representative long document rather than testing only a short page.

Why does a Playwright PDF look different from the browser?

  • Print media is active. Print rules may hide elements or alter colors. Select Media.Screen only when screen styling is the requirement.
  • Backgrounds are disabled. Enable the PDF background-printing option and check print CSS.
  • CSS page size is being ignored. Enable CSS page-size preference and remove conflicting API dimensions.
  • Fonts or images were not ready. Wait for the relevant selector or asset, then export.
  • Page breaks differ. Check margins, scale, @page, and break rules together; changing one can move content onto another page.

Browser installation and CI troubleshooting

“Executable doesn’t exist” or launch failure

The package is installed but its matching browser is not. Run the Playwright install script generated by your build, and run it again after a Playwright upgrade. Pin package versions in CI so the browser cache and .NET package stay aligned.

Linux reports missing libraries

Install the operating-system dependencies through the Playwright CLI using the browser-specific dependency option documented for your environment. Container images should include those libraries before the test or conversion job starts.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The PDF is empty or stops before dynamic content

Confirm that navigation reached the expected URL, wait for a page-specific readiness selector, and check that authentication or required headers are present. A page that fails in a normal headless browser session will not become valid merely because PdfAsync was called.

Colors or images are missing

Enable background printing, inspect @media print, and verify that assets load before export. Print output can modify colors; the CSS property -webkit-print-color-adjust can request exact color rendering, but validate the result in the PDF viewer you use.

Output ignores the declared paper size

Look for a conflict between Format, explicit width or height, and CSS @page. Set CSS page-size preference when the stylesheet is authoritative.

Reliability, performance, and security considerations

Reuse the browser process

For batches, launch Chromium once and create a new page or browser context per document. Repeated process startup is slower and consumes more resources. Close pages and contexts after each conversion, and close the browser in a finally path when wrapping this in a service.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Control resource usage

Very long pages, high-resolution images, and large scale values increase memory and PDF size. Set a navigation timeout appropriate to your page, use page ranges when a complete document is unnecessary, and avoid exporting more resolution than the reader needs.

Protect the conversion service

If users can submit URLs, restrict outbound access to prevent requests to internal services, validate allowed schemes, and isolate browser processes. Treat downloaded HTML, JavaScript, and generated PDFs as untrusted content. Supply authentication headers or cookies only for destinations you control.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If you need an HTTP service rather than a locally managed browser, ScreenshotNeo returns a PDF from one request. Its capture flow accepts cookie and consent banners before removing more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.

See the parameter reference in the ScreenshotNeo documentation. A basic cURL request is:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

For PDF output, request the PDF format using the documented format parameter:

curl -G "https://api.screenshotneo.com/v1/shot" 
  -d access_key=YOUR_API_KEY 
  --data-urlencode url=https://stripe.com 
  -d format=pdf 
  -o page.pdf

The same endpoint works from Python:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com", "format": "pdf"},
    timeout=90,
)
r.raise_for_status()
open("page.pdf", "wb").write(r.content)

And Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com', format: 'pdf' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('page.pdf', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. It supports paper size, margins, landscape mode, page ranges, custom CSS and JavaScript, waiting rules, headers, cookies, user agents, caching, async jobs, and bulk capture. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots, and every feature is on every plan. Create a free ScreenshotNeo account.

Frequently asked questions

Frequently Asked Questions

Can Playwright generate a PDF from HTML that is not hosted online?

Yes. Use SetContentAsync with the HTML string, then call PdfAsync. Include any CSS in the document or load stylesheets that the browser can reach.

Does PdfAsync return bytes as well as save a file?

Yes. The call returns PDF data; supplying the Path option also writes that data to the specified file.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Which browser should I install for PDF generation?

Use the browser binary installed by the Playwright version in your project and keep the package and binary installation synchronized. Chromium is the normal engine for this workflow.

Can I export only part of a PDF document?

Use the PDF page-range option for selected pages, or create a page containing only the section you want before exporting.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Read next

Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.