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.

To convert HTML to an image in C#, use PuppeteerSharp to load the markup in a headless browser, set a viewport, wait for fonts and other required assets, then call ScreenshotAsync. Use SetContentAsync for an HTML string and GoToAsync for an existing web page. The example below writes a full-page PNG to disk; later sections show in-memory output, URL capture, readiness checks, and common fixes.

Convert an HTML string to a PNG file

Install the PuppeteerSharp NuGet package in your C# project, then use the following top-level program. It provisions a compatible browser, launches it headlessly, loads the HTML, waits for fonts, and saves a full-page screenshot as output.png.

using PuppeteerSharp;

await new BrowserFetcher().DownloadAsync();
await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
    Headless = true
});

await using var page = await browser.NewPageAsync();
await page.SetViewportAsync(new ViewPortOptions
{
    Width = 1200,
    Height = 800,
    DeviceScaleFactor = 1
});

var html = """
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <style>body { font-family: Arial, sans-serif; margin: 0; }</style>
</head>
<body><h1>Rendered HTML</h1><p>Captured by PuppeteerSharp.</p></body>
</html>
""";

await page.SetContentAsync(html);
await page.EvaluateExpressionAsync("document.fonts.ready");
await page.ScreenshotAsync("output.png", new ScreenshotOptions
{
    FullPage = true
});

The example uses the official PuppeteerSharp browser-download, viewport, content-loading, font-readiness, and screenshot patterns. See the PuppeteerSharp documentation and its Page API reference for the API details.

What each step does

  1. BrowserFetcher().DownloadAsync() downloads a browser revision for PuppeteerSharp to use. Run it as part of setup or deployment provisioning rather than assuming a browser is already installed.
  2. LaunchAsync starts Chromium in headless mode. The browser and page are disposed asynchronously so their processes and resources are released.
  3. SetViewportAsync sets the CSS viewport to 1200 by 800 pixels. A device scale factor of 1 avoids extra high-density scaling in this example.
  4. SetContentAsync(html) renders the supplied markup in the page.
  5. document.fonts.ready waits for the document’s font-loading promise before capture.
  6. ScreenshotAsync("output.png", ...) writes the screenshot to a file. Setting FullPage to true captures beyond the visible viewport.

The API names and overloads can vary across package versions. Use the version installed in your project and consult its API reference if a code sample does not compile unchanged.

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

Choose the source: HTML markup or a web page

Render an HTML string

Use SetContentAsync(html) when your application has the markup in memory. This is convenient for reports, email previews, generated cards, or other content assembled by your program. The markup can include styles and scripts, but referenced assets must be accessible to the browser.

Capture an existing URL

For an already-hosted page, navigate directly instead of injecting markup:

await page.GoToAsync("https://example.com");
await page.EvaluateExpressionAsync("document.fonts.ready");
await page.ScreenshotAsync("page.png", new ScreenshotOptions
{
    FullPage = true
});

The official screenshot guide demonstrates navigation with GoToAsync followed by ScreenshotAsync. See PuppeteerSharp examples.

For HTML containing relative paths such as images/logo.png or styles/site.css, the browser needs a base URL or another way to resolve those assets. Use absolute asset URLs, or supply markup and resources through a setup that gives relative URLs a valid base. Check browser access, authentication, and network rules if remote assets are not loading.

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

Set framing and output format

Viewport screenshot versus full-page screenshot

By default, a screenshot reflects the visible page area. Set FullPage = true for a document-length image that includes content below the fold. A fixed viewport is usually the better framing for a thumbnail or interface card; full-page capture suits reports or long documents. The ScreenshotOptions API documents the full-page option.

Viewport size and pixel density

Set width, height, and device scale factor before rendering when output dimensions need to be repeatable. The viewport dimensions are CSS pixels; device scale factor affects the resulting image’s pixel density. Large viewports, full-page captures, or higher scale factors can produce much larger images and require more memory.

PNG and other screenshot formats

When saving to a path, the file extension determines the screenshot type; for example, use output.png for PNG. Choose another supported image extension when that format better suits your downstream use. Do not change the extension alone if your application expects a specific format—verify the actual output and consumer requirements.

Return the image in memory instead of saving a file

PuppeteerSharp provides screenshot methods for different delivery needs. Use a byte array for an API response or database write, base64 when another system expects encoded text, or a stream when you want to process the image incrementally.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Method Use it when
ScreenshotDataAsync You need image data in memory, such as to return from an application endpoint.
ScreenshotBase64Async The receiving interface specifically requires a base64 representation.
ScreenshotStreamAsync You want a stream-oriented output path rather than a file name.
ScreenshotAsync("path.png") You want PuppeteerSharp to write the image to disk.

Check the method signatures in the API reference for your installed package version. For an HTTP endpoint, return the bytes with the correct image content type, and avoid converting a large image to base64 unless the consumer needs it; base64 adds encoding overhead.

Wait for the page to be ready before capture

A screenshot can be technically successful but visually incomplete if a font, image, stylesheet, or client-rendered element has not appeared. Readiness is application-specific, so wait for the conditions that matter instead of relying on an arbitrary delay.

  • Fonts: Evaluate document.fonts.ready before capture, as in the sample.
  • Important elements: Wait for the target element or a page-specific readiness signal when the application renders asynchronously.
  • Images and styles: Confirm their URLs are reachable from the machine running the browser and that any required authentication is available.
  • Dynamic content: If the page changes after initial navigation, wait for the known change or signal before taking the screenshot.

One important limitation applies to markup injection: the PuppeteerSharp API reference says Networkidle0 and Networkidle2 wait conditions are not supported for SetContentAsync. Do not use those options as a readiness strategy for injected HTML; use an explicit signal or asset wait instead. This restriction is documented in the Page API reference.

Deployment, performance, and reliability

Provision the browser deliberately

The .NET package is an automation library; the rendering step still requires a compatible browser. Download or provision the browser revision before launch, as the official screenshot example does. In containers or restricted hosts, verify that the browser can start under the host’s permissions and sandbox configuration. The required operating-system libraries and launch configuration depend on the deployment environment, so check the actual runtime error rather than copying unrelated container settings.

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

Manage browser and page lifetimes

Dispose IBrowser and IPage with await using so browser processes do not remain alive after work completes. If your application captures many pages, plan resource use around page and browser lifetimes: full-page images and large device-scale factors increase capture memory and output size.

Make output reproducible

Keep the viewport, device scale factor, HTML, CSS, fonts, and asset versions consistent when comparing or caching screenshots. Browser rendering depends on layout and resources, so an unavailable remote font or a changed image can alter pixels even when the C# code has not changed. A fixed viewport helps control framing, but it cannot make external resources immutable.

Troubleshooting PuppeteerSharp screenshots

Symptom Likely cause Fix
Browser launch fails or no browser executable is found The compatible browser revision has not been downloaded or is unavailable in the deployment environment. Run browser provisioning before launch and confirm the executable is accessible to the application.
Screenshot is blank or incomplete The page has not rendered its content, or an asset failed to load. Wait for fonts and page-specific readiness; check asset URLs and browser network access.
Relative images or styles are missing The browser cannot resolve relative paths from the injected markup. Use absolute URLs or provide a valid base URL and ensure the rendering process can access the resources.
Screenshot ends at the viewport height The capture uses the visible viewport rather than the whole document. Set FullPage = true for a document-length capture.
Injected content wait condition is rejected Networkidle0 or Networkidle2 was requested with SetContentAsync. Use an explicit page readiness signal or asset/font wait; these network-idle conditions are not supported for that method.
Browser processes accumulate after requests Browser or page objects are not disposed when the capture finishes or fails. Use await using for both objects and ensure the scope is exited on error paths.
Output looks different between runs Viewport, fonts, assets, or dynamic page content differ. Fix viewport dimensions and scale, wait for required resources, and stabilize the page content where possible.
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 do not want to provision Chromium and manage rendering in your own C# service, ScreenshotNeo offers a screenshot API and MCP server. Its one-call API accepts a URL and returns an image or PDF. For example, cURL can save a WebP capture:

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

See the ScreenshotNeo API documentation for request options. Cookie and consent banners, newsletter popups, and chat widgets are removed before capture; those cleanup steps can each be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, with response headers reporting the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 shots.

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

Sign up for ScreenshotNeo’s free plan to try 1,000 screenshots a month with no card.

Frequently Asked Questions

Can PuppeteerSharp capture HTML that is not hosted online?

Yes. Pass the markup string to SetContentAsync; remote hosting is not required for the HTML itself, though any external assets still need to be reachable.

Can I return a screenshot from an ASP.NET endpoint?

Yes. Use an in-memory screenshot method such as ScreenshotDataAsync and return its image bytes with the appropriate content type.

Does PuppeteerSharp render CSS like a browser?

Yes. It renders the page in a headless browser, so browser layout and the availability of fonts, styles, and other assets determine the pixels.

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.

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.