How to Take a Playwright Screenshot Using C#
Capture viewport, full-page, or element screenshots with Playwright for .NET. Learn how to save files, tune output, and handle common capture issues.
To take a screenshot with Playwright for .NET, navigate a page and call Page.ScreenshotAsync. Set Path to save it as a file:
await page.ScreenshotAsync(new()
{
Path = "screenshot.png",
});
The default captures the visible viewport. Set FullPage = true for the full scrollable page, or call ScreenshotAsync on a locator to capture one element. See the Playwright .NET screenshots guide and ScreenshotNeo API documentation.
1. Set up a runnable C# screenshot
This console example launches Chromium, opens a page, waits for navigation, and writes a PNG. Start with a .NET console project, add the Playwright package, then install its browser:
dotnet new console -n PlaywrightShot
cd PlaywrightShot
dotnet add package Microsoft.Playwright
dotnet build
pwsh bin/Debug/net*/playwright.ps1 install chromium
Replace Program.cs with:
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", new()
{
WaitUntil = WaitUntilState.Load,
});
await page.ScreenshotAsync(new()
{
Path = "screenshot.png",
});
Console.WriteLine("Saved screenshot.png");
Run it with dotnet run. The relative output path is resolved from the process working directory. Use an absolute path if the application runs from a service or another directory than you expect.
2. Choose the capture area
Capture the viewport
Omitting FullPage captures the current viewport. Set the viewport before navigation if the layout depends on screen size:
var page = await browser.NewPageAsync(new()
{
ViewportSize = new() { Width = 1440, Height = 900 },
});
await page.GotoAsync("https://example.com");
await page.ScreenshotAsync(new() { Path = "viewport.png" });
Capture the full scrollable page
Set FullPage to include the page beyond the viewport:
await page.ScreenshotAsync(new()
{
Path = "full-page.png",
FullPage = true,
});
A full-page capture can be much taller and larger than a viewport image. Very long pages can take longer to render and may run into image-size or memory limits in your environment. For exceptionally long pages, consider capturing sections or changing the page to expose content in manageable portions.
Capture one element
Use a locator’s screenshot method to save just the matched element. Playwright scrolls it into view and waits for actionability checks. If the element is removed from the DOM before capture, the operation fails.
var header = page.Locator(".header");
await header.ScreenshotAsync(new()
{
Path = "header.png",
});
Prefer a stable selector, such as a test ID or unique CSS selector. If a selector matches multiple elements, make it specific or select the intended match explicitly.
Return bytes instead of writing a file
Omit Path to receive the screenshot as a byte array, useful for uploading, image processing, or visual comparisons:
byte[] imageBytes = await page.ScreenshotAsync();
await File.WriteAllBytesAsync("screenshot.png", imageBytes);
3. Configure image output and capture behavior
Playwright .NET exposes screenshot settings through PageScreenshotOptions and locator screenshot options. Check the API reference for the Playwright version installed in your project, especially for format support and defaults.
| Option | Purpose and practical choice |
|---|---|
Path |
Writes the result to a file. The extension can determine the format; omit it to get bytes instead. |
Type |
Selects PNG, JPEG, or WebP where supported by the installed version. PNG is lossless; JPEG is useful when smaller files matter; WebP offers another size and quality tradeoff. |
Quality |
Controls lossy image quality for JPEG and WebP. It does not apply to PNG. The API documents JPEG’s default as 80 and WebP quality 100 as lossless. |
Scale |
Css uses one output pixel per CSS pixel. Device scale uses device pixels and can produce larger high-DPI images. |
FullPage |
Captures the full scrollable page rather than the current viewport. |
Clip |
Restricts capture to a specified page rectangle when a precise region is needed. |
Mask |
Applies masks to locator-matched regions, useful for variable or sensitive content in repeatable captures. |
Animations |
Controls how animations are handled during capture. Disable or fast-forward them when stable test output matters. |
Caret |
Controls whether the text caret is hidden or rendered. |
OmitBackground |
Omits the default background where supported, useful for transparent output. |
Style |
Applies screenshot-only CSS, for example to hide a blinking cursor or a volatile element. |
Timeout |
Sets the screenshot operation timeout. Increase it only if the page needs more time to reach a capturable state. |
Example using a format, scale, and animation control:
await page.ScreenshotAsync(new()
{
Path = "stable.webp",
Type = ScreenshotType.Webp,
Quality = 85,
Scale = ScreenshotScale.Css,
Animations = ScreenshotAnimations.Disabled,
Timeout = 30_000,
});
WebP support was added to Playwright .NET; verify that the version in your project supports it. When using WebP, you can infer the format from a .webp path or select it explicitly.
4. Make captures repeatable
- Fix the environment. Use a known browser version, viewport, locale, and device scale factor if output is used for visual regression.
- Wait for the page state you need. Navigation completing does not guarantee that client-rendered data, fonts, or images have finished. Wait for a meaningful selector or app-specific ready signal before capture.
- Reduce visual changes. Disable animations or apply screenshot-only CSS to hide clocks, rotating banners, carets, and other volatile regions.
- Mask variable areas. Use masks for timestamps, avatars, or other content that should not affect a visual comparison.
- Keep dimensions consistent. Set viewport size before navigation, and choose CSS or device scale deliberately.
Screenshot options make capture behavior more controlled, but the resulting image still depends on the page state and the installed Playwright and browser versions.
5. Screenshot with cURL, Python, or Node.js
These examples call ScreenshotNeo’s screenshot API instead of launching a local browser. They use the same endpoint and return the response body as an image. Replace the example URL with the page you are authorized to capture, and keep the API key private.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com"},
timeout=90,
)
r.raise_for_status()
with open("shot.webp", "wb") as f:
f.write(r.content)
Node.js
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
For Node.js versions without Bun.write, save the response using Node’s filesystem API:
import { writeFile } from 'node:fs/promises';
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
6. Or skip the browser setup
One GET request to ScreenshotNeo returns a screenshot or PDF; see the API documentation for the available options.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://example.com \
-o shot.webp
- Cookie banners are accepted and removed, along with supported newsletter popups and chat widgets, before the shot.
- Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers report the page verdict and billing status.
- An MCP server lets AI agents use
take_screenshot,get_page_info, andcapture_pdf. - 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.
7. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Browser executable is missing | The Playwright package is installed, but its browser was not installed in this environment. | Run the Playwright install script for Chromium, or install the browser your code launches. |
| Navigation succeeds but the screenshot is blank or incomplete | The application renders after navigation, or content is lazy-loaded. | Wait for an app-specific ready selector or state, then capture. Scroll or otherwise trigger lazy content if the page requires it. |
| Screenshot times out | The page or screenshot work takes longer than the configured timeout. | Identify whether navigation or capture is timing out. Wait for a specific readiness condition and adjust the relevant timeout when justified. |
| Locator screenshot says the element is not attached | The matched element was replaced or removed between lookup and capture. | Wait for the locator to become visible and stable, then locate it again; avoid selectors tied to transient nodes. |
| Output is unexpectedly large | Full-page dimensions or device-pixel scaling multiply the number of output pixels. | Use viewport capture, CSS scale, or a lossy format and suitable quality when acceptable. |
| WebP format is rejected | The project uses a Playwright version without WebP screenshot support, or the format/path settings conflict. | Update to a supporting version or use PNG/JPEG; confirm the API reference for the installed version. |
| Visual tests vary between runs | Animations, dynamic data, fonts, viewport, or browser versions differ. | Pin the environment, wait for stable page state, disable animations, and mask volatile regions. |
8. Performance, reliability, and cost
A local Playwright capture uses a browser process, so browser startup, navigation, page rendering, and image encoding all contribute to latency. For repeated captures, reusing a browser while isolating pages can avoid repeated startup work; close pages and the browser when finished to release resources. Full-page and high-DPI images consume more memory and disk than viewport captures.
For reliable automation, handle navigation and capture errors explicitly, use bounded timeouts, and retry only failures that may be transient. A retry can repeat page side effects if the page performs actions, so keep screenshot flows read-only where possible. Pin Playwright and browser versions for visual tests.
Local Playwright has no per-screenshot API charge, but you operate the runtime and browser infrastructure. ScreenshotNeo offers a hosted option with a free allowance of 1,000 shots per month, then plans of $5 for 3,000, $15 for 15,000, $39 for 60,000, $99 for 250,000, and $249 for 1,000,000. Yearly billing gives two months free, and every feature is on every plan. Choose based on whether you prefer managing browser execution yourself or calling a hosted API.
9. Frequently asked questions
Does Playwright take screenshots by default?
No. Your code must call a screenshot method such as Page.ScreenshotAsync or Locator.ScreenshotAsync.
Can I capture a screenshot without saving a file?
Yes. Omit Path; the page screenshot method returns image bytes.
Can Playwright capture only part of a page?
Yes. Use a locator screenshot for an element, or configure a clip rectangle for a page region.
Which image format should I choose?
Use PNG for lossless output, or JPEG/WebP when a smaller lossy image is suitable. Confirm WebP availability for your Playwright version.
Why does my screenshot differ from what I see in my normal browser?
The viewport, device scale, browser version, page state, fonts, and dynamic content can change rendering. Make those inputs consistent for repeatable output.


