Playwright screenshots: full page, element & PDF

Working Playwright code for the screenshots people actually need, the fixes for the usual broken output, and an honest look at when an HTTP API saves you running browsers.

Full-page screenshot

Install with npm i playwright and npx playwright install chromium.

const { chromium } = require("playwright");

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage({ viewport: { width: 1280, height: 800 } });
  await page.goto("https://example.com", { waitUntil: "networkidle" });
  await page.screenshot({ path: "full.png", fullPage: true });
  await browser.close();
})();

fullPage: true captures the whole scrollable height. Without it you get just the viewport.

Screenshot one element

await page.locator("h1").screenshot({ path: "heading.png" });

The locator waits until the element is visible, scrolls it into view and crops to its bounding box. For a region, use page.screenshot({ clip: { x: 0, y: 0, width: 600, height: 300 } }).

Retina, JPEG, dark mode

const context = await browser.newContext({
  viewport: { width: 1280, height: 800 },
  deviceScaleFactor: 2,          // 2560px wide output
  colorScheme: "dark",           // prefers-color-scheme: dark
});
const page = await context.newPage();
await page.goto("https://example.com");
await page.screenshot({ path: "shot.jpg", type: "jpeg", quality: 80 });

PDF

await page.goto("https://example.com", { waitUntil: "networkidle" });
await page.pdf({ path: "page.pdf", format: "A4", printBackground: true,
  margin: { top: "1cm", bottom: "1cm", left: "1cm", right: "1cm" } });

page.pdf() only works in Chromium, and it renders with print CSS (@media print). Add await page.emulateMedia({ media: "screen" }) first if you want it to look like the screen.

Why the screenshot looks wrong (and the fixes)

The same thing as one HTTP call

If screenshots are a side feature, you may not want Chromium, fonts and a browser pool in your deployment. Shotmint runs Playwright for you:

// shot.mjs, run with: SHOTMINT_KEY=... node shot.mjs
import { writeFileSync } from "node:fs";

const params = new URLSearchParams({
  url: "https://example.com",
  full_page: "true",
  device_scale_factor: "2",
  format: "jpeg",
  quality: "80",
  block_ads: "true",               // also hides common cookie banners
});
const r = await fetch("https://shotmint.creasework.com/v1/screenshot?" + params, {
  headers: { "X-API-Key": process.env.SHOTMINT_KEY },
});
if (!r.ok) throw new Error(await r.text());
writeFileSync("full.jpg", Buffer.from(await r.arrayBuffer()));
PlaywrightShotmint
viewport: {width, height}width, height
fullPage: truefull_page=true (scrolls first, so lazy images load)
locator(sel).screenshot()selector=sel
deviceScaleFactor, colorScheme: "dark"device_scale_factor, dark_mode=true
style: ".banner { display: none }"hide_selectors=.banner, or block_ads=true for ads and cookie banners
type: "jpeg", qualityformat=jpeg (or webp), quality
page.pdf({format, landscape, printBackground})/v1/pdf with format, landscape, print_background

When to keep Playwright

Keep it for anything interactive: logging in, clicking through flows, filling forms, testing. Also keep it if you render at very high volume, or pages sit on a private network. The API is for when you just need the picture or the PDF. Using Selenium instead? See Selenium screenshots in Python. Want to try it without code? The full-page screenshot tool uses the same renderer.

Try it with 100 free renders

No signup, no card. Get a key in one click, then buy credits only if you need more (from €5 for 2,000, never expire).

Get a free API key Full API reference