Integrating PuppeteerSharp with C# in an MVC Application: A Practical Guide with Options and Code Examples Integrating PuppeteerSharp with C# in an MVC Application: A Practical Guide with Options and Code Examples

When you think of C# and web automation, your mind might first leap to Selenium. But what if I told you there's a headless browser automation tool with a slick, modern API, powered by the Chrome DevTools Protocol, and it's *blazingly fast*? Enter: PuppeteerSharp.


What is PuppeteerSharp?

PuppeteerSharp is a .NET port of the Node.js-based Puppeteer library. In other words, it's a C#-friendly wrapper that lets you control headless (or headed) Chrome or Chromium browsers.

Why would you want to use it? Well, PuppeteerSharp is ideal for:

* Generating PDFs or screenshots of webpages

* Crawling websites or extracting data

* Automated testing of front-end code

* Rendering JavaScript-heavy pages server-side (SSR) for SEO or previews

* And any other scenario where you'd want to simulate a real browser

Now imagine combining this power with the flexibility of an ASP.NET MVC application. Suddenly, your server-side app can moonlight as a front-end robot, churning out content with pixel-perfect rendering. Let's explore how to pull this off.

Option 1: Synchronous PDF Generation on Demand

Sometimes you just want to click a button and boom! — a PDF appears. This solution shows you how to trigger PuppeteerSharp to generate a PDF from a given URL right from your MVC controller.

What This Code Does

In this example, we'll integrate PuppeteerSharp into a controller action that renders a URL into a PDF and returns it as a file to the browser.


public async Task DownloadPdf()
{
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultChromiumRevision);
using var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true });
using var page = await browser.NewPageAsync();
```
await page.GoToAsync("https://example.com");
var pdfStream = await page.PdfStreamAsync(new PdfOptions
{
Format = PaperFormat.A4,
PrintBackground = true
});
return File(pdfStream, "application/pdf", "example.pdf");
```
}

Breakdown of the Code

* `BrowserFetcher().DownloadAsync(...)` ensures Chromium is downloaded. PuppeteerSharp doesn't come with Chromium out-of-the-box (probably to spare your SSD).

* `LaunchAsync(...)` starts a new headless Chromium instance.

* `NewPageAsync()` opens a new tab in the browser.

* `GoToAsync(...)` navigates to the URL you want to render.

* `PdfStreamAsync(...)` creates a PDF from the loaded page.

* `File(...)` returns the PDF stream to the client browser as a downloadable file.

Simple. Effective. Like having a personal PDF butler in your controller.

Option 2: Background Rendering Service with Dependency Injection

Want to level up your architecture? This solution encapsulates PuppeteerSharp in a service class and registers it for dependency injection (DI). This is great for large apps where concerns need to be cleanly separated.

Why Use a Service Class?

* Promotes testability

* Improves separation of concerns

* Centralizes browser instance reuse (yay for performance!)

Step 1: Create a PuppeteerService


public interface IPuppeteerService
{
Task\ GeneratePdfAsync(string url);
}
public class PuppeteerService : IPuppeteerService
{
private readonly Task \_browserTask;
```
public PuppeteerService()
{
_browserTask = InitBrowserAsync();
}
private async Task InitBrowserAsync()
{
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultChromiumRevision);
return await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true });
}
public async Task GeneratePdfAsync(string url)
{
var browser = await _browserTask;
using var page = await browser.NewPageAsync();
await page.GoToAsync(url);
return await page.PdfDataAsync(new PdfOptions
{
Format = PaperFormat.A4,
PrintBackground = true
});
}
```
}

Step 2: Register and Use the Service

In `Startup.cs` or wherever you configure DI:


services.AddSingleton\();

In your controller:


private readonly IPuppeteerService \_puppeteerService;
public HomeController(IPuppeteerService puppeteerService)
{
\_puppeteerService = puppeteerService;
}
public async Task GetPdf()
{
var data = await \_puppeteerService.GeneratePdfAsync("[https://example.com](https://example.com)");
return File(data, "application/pdf", "output.pdf");
}

What's Happening Here?

* The service class encapsulates all Puppeteer logic.

* `InitBrowserAsync()` downloads Chromium and launches a shared browser instance.

* `GeneratePdfAsync()` navigates to a URL and returns the resulting PDF bytes.

* In the controller, we inject and use the service just like any other dependency.

The benefit? You don't need to re-launch the browser each time — great for scalability and speed.

Option 3: Screenshot Generation for Previews or Thumbnails

PDFs not your thing? Maybe you're more of a visual learner. Or maybe your app generates preview thumbnails of blog posts, reports, or memes. PuppeteerSharp has you covered.

What This Code Does

This example shows how to capture a screenshot of a page and return it as a PNG file.


public async Task CaptureScreenshot()
{
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultChromiumRevision);
using var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true });
using var page = await browser.NewPageAsync();
```
await page.GoToAsync("https://example.com");
var imageBytes = await page.ScreenshotDataAsync(new ScreenshotOptions { FullPage = true });
return File(imageBytes, "image/png", "screenshot.png");
```
}

Explanation

* `ScreenshotDataAsync(...)` is your golden ticket to PNG heaven.

* The `FullPage = true` option ensures the entire webpage is captured — not just the visible viewport.

* The file is returned to the client browser as an image.

You could go fancier by setting viewport sizes or capturing only a specific DOM element. PuppeteerSharp is very flexible.

Tips, Gotchas, and Miscellaneous Wizardry

* Chromium Download Size: PuppeteerSharp downloads Chromium (\~100MB) on first use. Consider packaging it with your app or pointing to a custom executable if deploying to multiple servers.

* Headless = false: For debugging, launch with `Headless = false` and `Devtools = true`. Watching your automated browser do its thing is oddly satisfying.

* Concurrency: Launching a new browser instance for every request is expensive. Reuse a singleton browser where possible.

* Async/Await: All PuppeteerSharp methods are async. Your controller actions should be too.

PuppeteerSharp unlocks powerful browser automation directly from your ASP.NET MVC app. Whether you're generating PDFs, screenshots, or scraping content from dynamic pages, it's a sharp tool (pun fully intended) that's production-ready and fun to work with.

Whether you want quick-n-dirty PDF rendering or a clean, injectable service architecture, you've got options. Start small, play around, and before you know it, you'll be running a headless browser empire from your C# backend.

Happy coding — may your PDFs be crisp and your DOMs forever loaded!

Published on May 21, 2025

Tags: ASP.NET MVC and Web API Tutorial | c# | pdf | puppeteersharp

Related Posts

Did you enjoy this article? If you did here are some more articles that I thought you will enjoy as they are very similar to the article that you just finished reading.

Tutorials

Learn how to code in HTML, CSS, JavaScript, Python, Ruby, PHP, Java, C#, SQL, and more.

No matter the programming language you're looking to learn, I've hopefully compiled an incredible set of tutorials for you to learn; whether you are beginner or an expert, there is something for everyone to learn. Each topic I go in-depth and provide many examples throughout. I can't wait for you to dig in and improve your skillset with any of the tutorials below.