Library / SDK
hardkoded/puppeteer-sharp avatar
hardkoded/puppeteer-sharp

Puppeteer Sharp: the .NET port of Puppeteer, and what it costs you

Headless Chrome .NET API

3,922 stars484 forksC#MIT

At a glance

What is it?
Puppeteer Sharp brings the Node Puppeteer API to C# through three NuGet packages and a browser downloader. It is a good fit for .NET teams that already ship Chrome automation, and a poor fit for anyone who wants a single cross-browser driver.
Who is it for?
Adopt Puppeteer Sharp if your stack is .NET, your target is Chrome or Chromium, and you want the Puppeteer API rather than a WebDriver abstraction; the AspNetFramework companion package exists for older ASP.NET Classic hosts. Do not adopt it if you need one driver across Chrome, Firefox and Safari, or if you cannot run an X server on Linux.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 6 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 27, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The .NET gap Puppeteer Sharp fills

Puppeteer is a Node.js library for driving Chrome and Chromium over the DevTools protocol. Teams that write C# had two options before Puppeteer Sharp: shell out to a Node process, or use a WebDriver-based binding. The project's own description is blunt about the approach: it is a .NET port of the official Node.js Puppeteer API. That means the method names, the async shape and the page model are meant to look familiar to anyone who has read Puppeteer's Node documentation.

The audience is narrow but real. End-to-end test suites written in xUnit or NUnit, crawlers that need a real rendering engine, and services that turn HTML into PDF all sit inside .NET codebases where adding a Node runtime is an operational cost. Puppeteer Sharp keeps the browser automation in the same process space as the rest of the application, and the README points to API documentation, a Slack channel and Stack Overflow for support, plus a separate contribution guide.

One thing to be clear about: this is not a wrapper that calls out to the Node package. It is a reimplementation in C# that speaks the same protocol, which is why the API surface tracks Puppeteer releases closely and why the version numbers look like Puppeteer's.

Three packages, two target frameworks, one protocol

The repository publishes three NuGet packages, and the split matters more than it first appears. PuppeteerSharp is described as the full cross-browser automation tool. PuppeteerSharp.Cdp is aimed at Chrome-only AOT apps where binary size matters, which is a direct consequence of the release notes stating that AOT compilation is supported. PuppeteerSharp.AspNetFramework is the companion library for ASP.NET Classic hosts, so a legacy Web Forms or MVC 5 application is not locked out.

On the framework side, the README says the library comes in two flavors: a NetStandard 2.0 build for .NET Framework 4.6.1 and .NET Core 2.0 or greater, and a .NET 8 version. Choosing the wrong one is not a compile error you will catch early; it is a runtime surprise on a machine that only has one of the two installed.

The mechanism itself is the DevTools protocol. You launch or connect to a browser, open a page, and every call is an async round trip to that browser. The README shows a ConnectOptions type for attaching to a remote browser, which is how you separate the automation host from the browser host. That separation is what makes the Docker story workable: the browser can live in one container and the .NET service in another.

Installing Puppeteer Sharp and taking a first screenshot

Installation is a single NuGet package reference. The README's package table points at PuppeteerSharp on nuget.org for the cross-browser tool, and that is the one to start with.

bash
dotnet add package PuppeteerSharp

The first run needs a browser binary. Puppeteer Sharp does not assume one is installed; the README's screenshot example constructs a BrowserFetcher, calls DownloadAsync, and only then launches. The fetched browser is cached on disk, and the repository ships a sample directory named reuse-downloaded-chrome, which tells you the intended pattern is to download once and reuse rather than fetch per process.

cs
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("http://www.google.com");
await page.ScreenshotAsync(outputFile);

After this runs you should have a PNG at the path held in outputFile, and a downloaded Chrome build under the fetcher's cache directory. The await using declarations matter: they dispose the page and the browser when the scope ends, which is what actually terminates the browser process.

For PDF output the README adds two steps that are easy to skip and painful to debug. It recommends passing WaitUntilNavigation.Networkidle0 as a second parameter to GoToAsync when fonts load from a CDN, and it evaluates document.fonts.ready. The comment in the README is explicit that omitting the font wait can result in no text rendered in the PDF.

cs
await page.GoToAsync("http://www.google.com");
await page.EvaluateExpressionHandleAsync("document.fonts.ready");
await page.PdfAsync(outputFile);

If you need a different rendering size, SetViewportAsync takes a ViewPortOptions with Width and Height, shown in the README at 500 by 500.

Linux, X servers and the AOT trade-off

The prerequisites section is short and contains the sharpest constraint on the page: X-server is required on Linux. That is not a footnote. A headless container without an X server is the most common deployment shape for this kind of tool, and the README routes Linux Chrome problems to the Puppeteer repository's troubleshooting guide rather than documenting a fix itself. If you are putting this in a slim base image, you are on your own for the display layer, and the project's documentation does not walk you through it.

The AOT support announced in the 19 release notes is a genuine constraint too, in the sense that it forces a choice. PuppeteerSharp.Cdp exists specifically for Chrome-only AOT apps where binary size matters, which implies the full package carries weight that a trimmed, Chrome-only deployment does not need. If you pick the full package for an AOT build, you are paying for cross-browser code paths you will not use. If you pick the Cdp package, you give up the cross-browser claim.

The Firefox story is worth reading carefully. The recent news section points at the 21 release notes for testing the latest Firefox versions using WebDriver BiDi. That is a different transport from the DevTools protocol, and the README does not explain in the main body how the two coexist or what the limitations of the BiDi path are. Anyone whose primary requirement is Firefox should read those release notes in full before assuming parity with Chrome.

Puppeteer Sharp versus Playwright for .NET

The comparison people actually search for is against Playwright, and the difference is architectural rather than cosmetic. Playwright was designed from the start around a driver that speaks to Chromium, Firefox and WebKit through one API, with browser binaries managed by its own installer. Puppeteer Sharp inherits Puppeteer's shape: Chrome and Chromium are the primary target, Firefox arrives through WebDriver BiDi, and the browser binary is fetched by BrowserFetcher inside your code.

That last point changes how you deploy. With Puppeteer Sharp, downloading the browser is an explicit step you control, which is useful when you want to pin a specific Chrome build or reuse a cached download across processes, and awkward when you want the tooling to just handle it. The README's reuse-downloaded-chrome sample is the project's own answer to that awkwardness.

The API familiarity argument cuts the other way. If your team already knows Puppeteer from Node, Puppeteer Sharp will feel like the same library, down to method names like GoToAsync and EvaluateFunctionAsync. If your team knows neither, Playwright's single cross-browser model is less to learn. Neither choice is about raw capability; it is about which browser set you must cover and who maintains the binary.

Licence and the cost of tracking upstream

Puppeteer Sharp is MIT licensed. The practical consequence is that you can ship it inside a closed-source product, modify it, and redistribute it, provided you keep the copyright notice and permission text. That is the standard MIT bargain, and it is the same licence the Node Puppeteer project uses, so there is no licensing split between the two if your organisation already approved one.

What the licence does not cover is the browser. BrowserFetcher downloads a Chrome or Chromium build, and that binary has its own terms, which are separate from the MIT licence on this library. The README does not discuss this, so it is worth checking with whoever handles third-party components before you ship a product that fetches a browser at runtime.

Upgrade cost is the other ongoing expense. The release cadence visible in the repository is roughly every two to three weeks, and version numbers track Puppeteer's own line. That is good for API currency and bad for stability: a minor version bump can carry protocol changes from upstream. Pinning a version and reading release notes before moving is the realistic posture. The repository also carries an UPSTREAM-15292.md file at the top level, which suggests upstream Puppeteer issues are tracked deliberately rather than absorbed silently.

Editorial conclusion

Adopt Puppeteer Sharp if your stack is .NET, your target is Chrome or Chromium, and you want the Puppeteer API rather than a WebDriver abstraction; the AspNetFramework companion package exists for older ASP.NET Classic hosts. Do not adopt it if you need one driver across Chrome, Firefox and Safari, or if you cannot run an X server on Linux. Before committing, verify that your target framework matches the NetStandard 2.0 or .NET 8 flavor, and check whether a single Chrome-only package such as PuppeteerSharp.Cdp fits your deployment size better than the full one.

Frequently asked questions

How do I use Puppeteer Sharp in a C# project?

Add the PuppeteerSharp NuGet package, create a BrowserFetcher and call DownloadAsync to get a browser binary, then launch with Puppeteer.LaunchAsync and open a page with NewPageAsync. The README's screenshot example runs through exactly that sequence in six lines.

What is Puppeteer Sharp?

It is a .NET port of the official Node.js Puppeteer API, published as a NuGet package for driving Chrome and Chromium from C#. The README describes the full package as a cross-browser automation tool, with a Chrome-only variant for AOT apps.

Is Puppeteer Sharp free to use?

Yes. The repository is MIT licensed, which permits commercial and closed-source use as long as the copyright and permission notice are retained. That licence covers the library, not the browser binary that BrowserFetcher downloads.

Official sources

  1. hardkoded/puppeteer-sharp on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/hardkoded-puppeteer-sharp.svg)](https://hysenlabs.com/projects/hardkoded-puppeteer-sharp)