Tested Puppeteer 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 Chrome.
Install with npm i puppeteer. It downloads a matching Chrome into ~/.cache/puppeteer.
const puppeteer = require("puppeteer");
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 1280, height: 800 });
await page.goto("https://example.com", { waitUntil: "networkidle2" });
await page.screenshot({ path: "full.png", fullPage: true });
await browser.close();
})();
fullPage: true captures the whole scrollable height. Without it you get just the 1280×800 viewport. Puppeteer's default viewport is 800×600, so always set one.
const heading = await page.waitForSelector("h1");
await heading.screenshot({ path: "heading.png" });
await page.screenshot({ path: "region.png", clip: { x: 0, y: 0, width: 600, height: 300 } });
waitForSelector returns an element handle once it's in the DOM. Its screenshot() scrolls it into view and crops to its bounding box.
await page.setViewport({ width: 1280, height: 800, deviceScaleFactor: 2 }); // 2560px wide
await page.emulateMediaFeatures([{ name: "prefers-color-scheme", value: "dark" }]);
await page.goto("https://example.com");
await page.screenshot({ path: "shot.jpg", type: "jpeg", quality: 80 });
type also takes "webp". quality only applies to JPEG and WebP.
await page.goto("https://example.com", { waitUntil: "networkidle2" });
await page.emulateMediaType("screen"); // optional: look like the screen, not print CSS
await page.pdf({ path: "page.pdf", format: "A4", printBackground: true,
margin: { top: "1cm", bottom: "1cm", left: "1cm", right: "1cm" } });
Without printBackground background colors and images are dropped. By default Chrome uses your @media print styles.
page.waitForTimeout is not a function: it was removed in Puppeteer 22. Use await new Promise(r => setTimeout(r, 1000)), or better, wait for what you need with page.waitForSelector().fullPage doesn't scroll. Scroll first: await page.evaluate(async () => { for (let y = 0; y < document.body.scrollHeight; y += 600) { window.scrollTo(0, y); await new Promise(r => setTimeout(r, 100)); } window.scrollTo(0, 0); });await page.addStyleTag({ content: "#onetrust-banner-sdk { display: none !important } *, *::before, *::after { animation: none !important; transition: none !important }" }).addStyleTag trick with position: static !important on the header.puppeteer.launch({ args: ["--no-sandbox"] }) (only for pages you trust), or run as a non-root user with the sandbox set up.PUPPETEER_SKIP_DOWNLOAD). Run npx puppeteer browsers install chrome, or point PUPPETEER_EXECUTABLE_PATH at an installed Chrome.apt install fonts-noto-cjk fonts-noto-color-emoji.networkidle0 times out: pages with analytics beacons or websockets never go fully quiet. Use networkidle2 (at most 2 open connections) or load plus waitForSelector.type: "jpeg" with a quality, or compress afterwards.If screenshots are a side feature, you may not want Chrome, fonts and a browser pool in your deployment. Shotmint runs the browser 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", // scrolls first, so lazy images load
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()));
| Puppeteer | Shotmint |
|---|---|
setViewport({width, height, deviceScaleFactor}) | width, height, device_scale_factor |
screenshot({fullPage: true}) | full_page=true |
(await page.waitForSelector(sel)).screenshot() | selector=sel |
goto(url, {waitUntil}) | wait_until=load|domcontentloaded|networkidle, plus delay (ms, up to 10000) |
emulateMediaFeatures(dark) | dark_mode=true |
addStyleTag({content: ".x{display:none}"}) | hide_selectors=.x, or block_ads=true |
page.pdf({format, landscape, printBackground}) | /v1/pdf with format, landscape, print_background |
Keep it for anything interactive: logging in, clicking through flows, filling forms, scraping. Also keep it at very high volume, or for pages on a private network. The API is for when you just need the picture or the PDF. More: Puppeteer vs. an API, Playwright screenshots, Selenium in Python, plain chrome --headless. No code at all: the full-page screenshot tool.
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