Playwright takes a screenshot with one method: page.screenshot(). Point it at a page that has finished loading, pass a path or read the returned buffer, and you have a PNG of the viewport.
By the end you'll capture the viewport, the full scrollable page, a single element, and a clipped region, and you'll know when running the browser yourself stops being worth it.
If you would rather not own a Chromium binary at all, the whole job is one HTTP request to the ScreenshotRender API: https://screenshotrender.com/api/v1/screenshot?apiKey=YOUR_API_KEY&url=https://news.ycombinator.com&fullPage=true. That returns a hosted image URL with no browser to launch, install, or scale. The rest of this post covers the Playwright recipes first, then where each one breaks and when the API call is the cheaper answer. If you're still choosing between the two libraries, our Puppeteer vs Playwright comparison breaks down the capture differences.
How do you take a screenshot in Playwright?
You take a screenshot in Playwright by calling page.screenshot() after the page has loaded. The full chain is launch a browser, open a page, navigate, capture, close.
In prose: const browser = await chromium.launch(), then const page = await browser.newPage(), then await page.goto("https://news.ycombinator.com"), then await page.screenshot({ path: "hn.png" }), then await browser.close(). The page.screenshot() API returns a Buffer when you omit path, so you can pipe the bytes straight into a database, an S3 upload, or an HTTP response instead of writing a file.
The default capture is the browser viewport, which Playwright sizes at 1280 by 720 unless you set viewport on the context. That default is the first thing that surprises people, so it's worth pinning down next.
Why does the default screenshot only capture the viewport?
The default screenshot only captures the viewport because page.screenshot() photographs what is currently visible, not the entire document. A page that scrolls for ten screens still returns a single 1280 by 720 image unless you ask for more.
That is the wrong way to grab a long page, and it is the most common reason a Playwright screenshot looks truncated. The fix is one option, covered in the next section. Keep the viewport capture for fixed-height UI like a hero section or a single card, where photographing the whole document would just add empty space.
How do you capture a full page screenshot in Playwright?
You capture a full page screenshot in Playwright by passing fullPage: true to the screenshot call: await page.screenshot({ path: "full.png", fullPage: true }). Playwright measures the document's total scroll height and stitches the capture from top to bottom, no matter how long the page is.
The Playwright screenshots guide documents the rest of the options on the same call. The ones worth knowing:
fullPage: truecaptures the entire scrollable document instead of the viewport.type: "jpeg"with aqualitynumber trades file size for fidelity; PNG is the default and is lossless.clip: { x, y, width, height }captures an arbitrary rectangle, which we'll use for partial captures below.animations: "disabled"freezes CSS animations and transitions so the capture is deterministic.
How do you screenshot a single element in Playwright?
You screenshot a single element in Playwright by calling screenshot() on a locator instead of the page: await page.locator(".price-card").screenshot({ path: "card.png" }). Playwright scrolls the element into view and crops the capture to its bounding box.
The Locator.screenshot() API is the right tool for OG cards, chart components, or a single pricing table you want as an image. When you need a fixed region rather than a DOM element, reach for clip instead: await page.screenshot({ clip: { x: 0, y: 0, width: 800, height: 420 } }) captures the top-left 800 by 420 rectangle regardless of what markup sits there.
At this point you can capture almost anything. The catch is everything around the capture: keeping Chromium installed on every server and CI runner, handling the pages that fight back, and paying for the machine time whether the render succeeds or not.
Skip the Chromium build, the Cloudflare fight, and the EC2 fleet.
ScreenshotRender renders the page on its infrastructure and strips cookie banners, ads, and chat widgets before every capture. Pass a URL, get a clean PNG back. 100 free screenshots a month, no credit card, and a credit is only spent on a successful render.
Get an API keyWhy does a Playwright screenshot come out blank or cut off?
A Playwright screenshot comes out blank or cut off when the capture fires before the page finished rendering, so fonts, lazy images, or animated content are not on screen yet. The capture is correct; the timing is wrong.
Three fixes handle most of these, in order of how often you'll need them:
- Wait for the network to settle.
await page.waitForLoadState("networkidle")holds until there are no in-flight requests, which covers most data-driven pages. The waitForLoadState API documents the other states. - Wait for the thing you care about.
await page.locator(".chart").waitFor()is more precise than a blanket network wait and faster than a hard-coded sleep. - Trigger lazy content before a full page capture. Images that load on scroll never appear in a
fullPageshot if nothing scrolled. Scroll to the bottom, wait, then capture, or the bottom of the image comes back empty.
Retina sharpness is a separate setting. Set deviceScaleFactor: 2 on the browser context to capture at 2x density, the same idea as the browser's devicePixelRatio. And if the screenshot call itself throws Execution context was destroyed, that is a navigation race, not a timing issue, and it has its own fix in our guide to the execution context was destroyed error.
When should you use a screenshot API instead of Playwright?
Use a screenshot API instead of Playwright when you don't want to own the browser infrastructure: the Chromium binary on every host, the memory ceiling, the bot-protection arms race, and the autoscaling. DIY Playwright is the right call when you need full browser control or you're already running it for end-to-end tests. For everything else, one HTTP call is less to maintain.
That is where ScreenshotRender fits. The same capture you just wrote in Playwright becomes one request: https://screenshotrender.com/api/v1/screenshot?apiKey=YOUR_API_KEY&url=https://news.ycombinator.com&fullPage=true. The API key is the sr--prefixed key from your dashboard, url is the target page, and fullPage=true mirrors Playwright's fullPage option. The JSON response carries the hosted image URL at data.screenshot.
The reasons teams move the render off their own servers map directly to the Playwright pain points above:
- No browser to install or scale. There is no Chromium download, no missing-font surprise in an Alpine container, no headless flag to tune.
- Clean captures by default. Cookie consent banners, ad overlays, and chat widgets are removed before every capture, so you skip the element-hiding code you would otherwise write in Playwright.
- Bot protection handled. A vanilla Playwright browser gets a challenge page on Cloudflare-protected sites; Stealth Mode on the Hobby plan and above renders the real page instead. The full picture is in our guide to screenshotting Cloudflare-protected websites.
- You only pay for what renders. A credit is spent only after a successful capture, and a built-in 2 second wait handles most JavaScript-heavy pages before the shot fires.
The free plan covers 100 screenshots a month at 40 requests per minute with no credit card; the Hobby plan raises that to 2,000 captures and 60 requests per minute. The full breakdown is on the pricing page.
Common questions about Playwright screenshots
How do I take a screenshot in Playwright Python?
The Python API mirrors the Node.js one. Launch a browser with sync_playwright, open a page, call page.goto(url), then page.screenshot(path="shot.png", full_page=True). The method names use snake_case (full_page instead of fullPage), but the capture behavior is identical to the JavaScript version.
How do I take a screenshot on test failure in Playwright?
Set screenshot: "only-on-failure" in the use block of playwright.config.js. The Playwright test runner then captures and attaches a screenshot automatically whenever a test fails, with no extra code in the test body. You can also call page.screenshot() inside an afterEach hook when you want custom file names or destinations.
Why is my Playwright full page screenshot cut off?
A full page screenshot gets cut off when lazy-loaded content has not rendered yet, or when a fixed CSS height sits on a scroll container instead of the document. Scroll to the bottom first to trigger lazy images, wait for network idle, and make sure the page itself owns the scroll rather than an inner div. The fullPage option captures the document scroll height, so content that never loaded never appears in the image.
Can Playwright screenshot a Cloudflare-protected page?
Often no. A vanilla Playwright browser is served a Cloudflare challenge page instead of the real content, so the screenshot captures the interstitial. You can add stealth plugins and real fingerprints, or use a service that ships bot bypass built in. ScreenshotRender includes Stealth Mode on the Hobby plan and above for exactly this case.
The honest takeaway: Playwright's page.screenshot() and locator.screenshot() cover every capture mode you need, and they're the right tool when you already run the browser. The moment you find yourself maintaining Chromium across hosts, fighting Cloudflare, or paying for failed renders, one API call is the lower-maintenance path to the same PNG.



