How to Capture a Webpage Screenshot with Playwright in .NET
Capture viewport, full-page, or element screenshots with Playwright .NET. Save images to disk or bytes, configure output, and fix common browser setup issues.
To capture a webpage screenshot with Playwright in .NET, navigate a Page to the URL and call ScreenshotAsync. By default, Playwright captures the visible viewport. Set FullPage = true to capture the full scrollable page, or call ScreenshotAsync on a locator to capture one element.
1. Install Playwright and its browser
Add the Playwright package to your .NET project, then build the project so its generated browser installation script is available. Install a browser binary that matches the Playwright package version. For Chromium, the documented setup command is:
pwsh bin/Debug/netX/playwright.ps1 install chromium
Replace netX with your project’s target framework directory. Omit chromium to install the default browsers, or use a supported engine such as Firefox or WebKit. After updating Playwright, rerun the install command if the browser executable is missing or out of sync. See the Playwright .NET browser installation guide and getting started documentation.
2. Capture a viewport screenshot
This complete minimal console program launches Chromium, opens a page, navigates to a site, and saves the visible viewport as a PNG. Create a .NET console project, add the Playwright package, build it, install Chromium as above, then run it.
using Microsoft.Playwright;
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync();
var page = await browser.NewPageAsync();
await page.GotoAsync("https://example.com");
await page.ScreenshotAsync(new() { Path = "screenshot.png" });
await page.CloseAsync();
NewPageAsync() is convenient for a small example. For production code, create a BrowserContext explicitly so you can control its settings and lifetime, then create the page from that context:
using Microsoft.Playwright;
using var playwright = await Playwright.CreateAsync();
await using var browser = await playwright.Chromium.LaunchAsync();
await using var context = await browser.NewContextAsync(new()
{
ViewportSize = new() { Width = 1440, Height = 900 }
});
var page = await context.NewPageAsync();
await page.GotoAsync("https://example.com");
await page.ScreenshotAsync(new() { Path = "screenshot.png" });
A relative output path is resolved from the process’s current working directory. Use an absolute path when the location must be unambiguous. Playwright infers the image type from the extension unless you set Type explicitly. The official screenshot documentation describes the page and locator screenshot APIs.
3. Choose what to capture
Visible viewport
The default is the currently visible viewport. Set the viewport when you need a predictable image size; it is a browser context setting:
await using var context = await browser.NewContextAsync(new()
{
ViewportSize = new() { Width = 1280, Height = 800 }
});
Full scrollable page
Set FullPage to capture the entire page as one tall image:
await page.ScreenshotAsync(new()
{
Path = "full-page.png",
FullPage = true
});
This does not divide the page into printable pages. Very long pages can produce large images and take longer to encode or transfer.
One element
Use Locator.ScreenshotAsync to capture the matched element’s bounds. The locator screenshot waits for actionability and scrolls the element into view.
await page.Locator(".header").ScreenshotAsync(new()
{
Path = "header.png"
});
Choose a selector that identifies one intended element. If it matches no element, the operation times out; if it matches multiple elements, use a more specific locator or select the intended match. Covered portions remain covered in the result. For a scrollable container, the screenshot includes only the content currently scrolled into view, not the container’s entire scrollable contents.
Rectangular clipped area
For a fixed region of the page, set Clip with coordinates and dimensions:
await page.ScreenshotAsync(new()
{
Path = "region.png",
Clip = new() { X = 100, Y = 80, Width = 600, Height = 400 }
});
Coordinates describe the page area in CSS pixels. Ensure the clip dimensions are positive and the rectangle fits the intended content.
4. Save to a file or use the image bytes
Supply Path to write an image file. If you omit it, ScreenshotAsync returns a byte[], which you can pass to an image-processing library, upload, or compare in a visual test:
byte[] screenshot = await page.ScreenshotAsync();
await File.WriteAllBytesAsync("screenshot.png", screenshot);
The documented image formats are PNG, JPEG, and WebP. WebP support depends on the Playwright version; it is listed in the Playwright .NET 1.62 release notes. Check the installed package version if Webp is unavailable or a format request fails. See the Page screenshot API options for the current enum names and supported options.
5. Configure screenshot output
| Option | What it controls | Practical note |
|---|---|---|
Type |
PNG, JPEG, or WebP encoding | When omitted, a path extension determines the format; without a path, use the option if a particular encoding is needed. |
Quality |
Lossy encoding quality | JPEG accepts 0–100 and defaults to 80. It does not affect PNG. WebP quality 100 is lossless; lower values are lossy. |
Scale |
Output pixel scale | Css uses one output pixel per CSS pixel. Device uses device pixels and can create larger output on high-DPI displays; the API default is Device. |
OmitBackground |
Transparent page background | Useful for transparent PNG or WebP output; it does not apply to JPEG. |
Animations |
Animation handling during capture | Disabled fast-forwards finite animations and cancels infinite animations for the capture before allowing them to continue. |
Style |
CSS injected for the screenshot | Use to hide dynamic content or normalize presentation without permanently changing the page. |
Mask |
Locator regions covered in the screenshot | Useful for timestamps or other variable regions in image comparisons. |
| Caret control | Whether a text caret appears | Set the documented caret option when a focused input’s blinking cursor would make captures inconsistent. |
Timeout |
Maximum screenshot operation time | The documented default is 30,000 ms. Set 0 to disable the screenshot timeout; a page or context can also set a default. |
Example combining output options:
await page.ScreenshotAsync(new()
{
Path = "stable-shot.webp",
Type = ScreenshotType.Webp,
Quality = 85,
Scale = ScreenshotScale.Css,
Animations = ScreenshotAnimations.Disabled,
Timeout = 30_000
});
Enum names can vary with package version. If an option does not compile, check the API reference for the version installed in the project. Choose PNG for lossless output and pixel comparisons, JPEG for smaller photographic images, and WebP when the installed version and downstream consumers support it.
6. Make captures more repeatable
A screenshot captures rendered browser state, so the same URL can produce different output as fonts, images, animations, ads, or user-specific content change. For more consistent results:
- Set an explicit viewport and device scale behavior.
- Wait for the page state your capture needs. For example, wait for a key locator instead of assuming navigation means every client-rendered component is ready.
- Disable animations for the capture when motion is irrelevant.
- Use
StyleorMaskto handle known dynamic areas in visual comparisons. - Use a fresh context when tests need isolated cookies and storage; reuse a context when the same authenticated session is intentionally needed.
Navigation completion does not guarantee every lazy-loaded image or application request is finished. Choose an explicit readiness condition that matches the site. For full-page captures of pages that load content while scrolling, make the page load that content before capture; the screenshot option itself should not be treated as a guarantee that every site’s lazy content has loaded.
7. Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Browser executable not found | The browser binary has not been installed for this Playwright version, or the package was updated after installation. | Build the project and run its generated Playwright install script again for the browser you launch. |
| Screenshot times out | The page, locator, or screenshot operation did not reach the required state before its timeout. | Wait for the specific content you need, check that the locator exists, and adjust the relevant timeout. Setting screenshot timeout to 0 disables that particular timeout. |
| Output file is missing | The path is relative to a different working directory than expected, or the destination directory does not exist. | Log the current directory, use an absolute path, and create the destination directory first. |
| Screenshot is blank or incomplete | The page was captured before its content rendered, navigation failed, or content is inside an unhandled frame or lazy-loading region. | Check navigation outcome and wait for a meaningful locator or app-ready state before capturing. |
| Element screenshot includes an overlay | A dialog, sticky header, or other element covers the target pixels. | Dismiss or hide the overlay, or capture a less obstructed region. Locator capture does not reveal pixels hidden behind another element. |
| Full-page image is unexpectedly large | The page is tall and the screenshot uses device-pixel scaling. | Consider Scale = ScreenshotScale.Css, a clipped region, or a viewport shot if that is the intended deliverable. |
| WebP enum or output is unavailable | The installed Playwright .NET version predates WebP support or differs from the documentation being followed. | Check the package version and its matching API reference; WebP is called out in the .NET 1.62 release notes. |
8. Performance, reliability, and cost
Playwright runs a real browser, so capture work includes browser startup, page navigation, rendering, image encoding, and file or network output. Reuse a browser across multiple captures when appropriate, while keeping context lifetimes deliberate. A shared context also shares its cookies and storage, so isolate work that requires separate sessions. Full-page captures and device-pixel scaling can increase image dimensions, memory use, and output size. JPEG or lossy WebP can reduce image size when visual fidelity requirements allow it.
For reliable automation, handle navigation failures and timeouts, wait on the content that matters, and close pages, contexts, and browsers when finished. Pin and deploy the Playwright package together with its matching browser binaries. The documented approach uses Playwright and installed browser binaries; the research sources identify no separate per-screenshot charge for this workflow. Infrastructure and execution costs depend on where and how you run it.
9. Or skip the browser setup
If you need a screenshot from an API call instead of installing and managing browsers, ScreenshotNeo is a website screenshot API and MCP server. Its API accepts a URL and returns an image or PDF. See the ScreenshotNeo API documentation.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));
ScreenshotNeo removes cookie banners, popups, and chat widgets before capture. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Sign up for 1,000 free screenshots a month, with no card required.
10. FAQ
Can I return screenshot bytes instead of writing a file?
Yes. Omit Path and use the returned byte[] directly or write it to a destination yourself.
Does a full-page screenshot produce a PDF?
No. FullPage = true produces one tall image. Use Playwright’s PDF functionality separately when you need paginated document output.
Can I capture an element inside a scrollable container?
You can capture a matched element, but a locator screenshot captures its currently visible scrolled content. Scroll the container to the desired position first if you need another portion.
Which browser engine should I use?
Use the engine that matches your testing or rendering target. The setup example uses Chromium; Playwright .NET also supports Firefox and WebKit.


