Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

The practical answer: run a real browser from TypeScript, navigate to the URL, wait until the page is ready for your use case, and call page.screenshot(). Playwright and Puppeteer both support viewport, full-page, element, clipped, file, and in-memory screenshots. Use Playwright when its locator and test-runner workflow fits your project; use Puppeteer when your existing tooling is built around it. Neither is universally faster or more accurate based on the documented APIs alone.

Choose the capture scope first

Decide what “screenshot” means before writing code. A normal screenshot captures the current viewport. A full-page capture includes the document beyond the visible window. An element capture targets one selector, and a clipped capture uses explicit coordinates.

Goal Playwright Puppeteer
Visible viewport page.screenshot() page.screenshot()
Entire scrollable page fullPage: true fullPage: true
One element page.locator(selector).screenshot() Element handle .screenshot()
Rectangular region clip: { x, y, width, height } clip: { x, y, width, height }
Return bytes Omit path Omit path

Set up TypeScript with Playwright

Install Playwright and its browser binaries in your project:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
npm install -D playwright typescript tsx
npx playwright install chromium

The following complete script creates a 1,440×900 page, waits for navigation, and writes a PNG:

import { chromium } from 'playwright';

const browser = await chromium.launch();
const page = await browser.newPage({
  viewport: { width: 1440, height: 900 }
});

await page.goto('https://example.com', { waitUntil: 'networkidle' });
await page.screenshot({ path: 'example.png' });
await browser.close();

Run it with npx tsx screenshot.ts. The relative path is resolved from your current working directory. Always close the browser in production code, including error paths (for example, with try/finally), so failed jobs do not leave Chromium processes behind.

Capture the full page

await page.screenshot({
  path: 'full-page.png',
  fullPage: true
});

fullPage is false by default. Full-page rendering can be tall and memory-intensive on long documents. If a site only loads images while scrolling, scroll the page or wait for the images before capturing; otherwise the result can contain unloaded placeholders.

Capture one element

await page.locator('header.site-header').screenshot({
  path: 'header.png'
});

The locator must resolve to a visible element. Prefer a stable class, data attribute, or accessible locator rather than a position-dependent selector.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capture a clipped rectangle

await page.screenshot({
  path: 'crop.png',
  clip: { x: 80, y: 120, width: 640, height: 400 }
});

The rectangle is measured in CSS pixels relative to the page. It must fit the rendered page; calculate coordinates after the layout is ready.

Keep the image in memory

const bytes = await page.screenshot({ type: 'png' });
// bytes is a Buffer that can be uploaded or returned by an API

Use Puppeteer instead

Install Puppeteer and its TypeScript runner:

npm install puppeteer typescript tsx

This script follows Puppeteer’s documented navigation pattern and saves a PNG:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.setViewport({ width: 1440, height: 900 });
  await page.goto('https://example.com', { waitUntil: 'networkidle2' });
  await page.screenshot({ path: 'example.png' });
} finally {
  await browser.close();
}

As with Playwright, add fullPage: true for the full document, omit path for image bytes, and use an element handle for a component. Puppeteer infers the output type from the filename extension and uses PNG by default.

const card = await page.$('.pricing-card');
if (!card) throw new Error('pricing card not found');
await card.screenshot({ path: 'pricing-card.webp', type: 'webp', quality: 82 });

Make the capture deterministic

Navigation completion is not the same as visual readiness. A page can continue hydrating, animating, polling, opening consent dialogs, or lazy-loading images after the network appears idle.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Wait for an application signal

await page.goto('https://example.com/dashboard', { waitUntil: 'domcontentloaded' });
await page.locator('[data-page-ready="true"]').waitFor();
await page.screenshot({ path: 'dashboard.png' });

Choose a selector that your application sets only after the important content exists. For a one-off page with no reliable signal, a short, deliberate delay is less fragile than assuming a universal “ready” event:

await page.waitForTimeout(1000);

Use delays sparingly: they slow every capture and can still fail when server or device speed changes. Disable or wait out animations when pixel consistency matters. Cookie dialogs, chat widgets, and newsletter popups should be dismissed or hidden before the screenshot; otherwise they become part of the output.

Control viewport and pixel density

Set the viewport explicitly so captures are reproducible. In Playwright, scale: 'css' produces one output pixel per CSS pixel, while scale: 'device' follows device-pixel density. Use the latter for a retina-like image and the former for predictable file dimensions.

await page.screenshot({
  path: 'retina.png',
  scale: 'device'
});

Choose an image format

  • PNG: lossless and suitable for text, diagrams, and transparency.
  • JPEG: smaller for photographic pages; set quality when supported. JPEG cannot store transparency.
  • WebP: often a compact web-friendly choice; set quality when your chosen library supports it.

Playwright can omit the background with omitBackground: true; Puppeteer exposes the same control. Use PNG or another transparency-capable format when you need an alpha channel.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Useful TypeScript patterns

Reliable cleanup

import { chromium } from 'playwright';

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

Authenticated pages

Log in through the browser or provide the session state your application already uses. Do not hard-code credentials in source control. If a screenshot job runs in a service, isolate each browser context so one user’s cookies cannot leak into another capture.

Stable visual output

  • Set viewport, locale, timezone, and color scheme explicitly.
  • Wait for fonts and important images to finish loading.
  • Freeze or disable animations when comparing images.
  • Use deterministic test data and a fixed URL revision.
  • Give every job a timeout and record the URL and failure reason.

Playwright or Puppeteer?

Decision factor Playwright Puppeteer
Locator style Rich locator API, including page.locator() Selectors and element handles
Readiness ergonomics Locator waits are convenient for application states Navigation waits plus explicit waits
Screenshot scope Viewport, full page, element, clip Viewport, full page, element, clip
Output File or bytes File or bytes
Best choice Your project already uses Playwright or benefits from its locator/test workflow Your project already uses Puppeteer or its conventions

The documented capabilities overlap substantially. Select based on browser coverage, existing project conventions, navigation and locator ergonomics, test-runner integration, and how your application stores or uploads image bytes—not on an unsupported claim that one library always renders better.

Troubleshooting common failures

The screenshot is blank or shows a loading shell

Cause: capture happened before hydration, data fetching, or lazy images completed. Fix: wait for a page-specific ready selector, an application event, or the required image count; then capture.

A cookie banner or chat box covers the page

Cause: overlays are normal page content. Fix: click the consent action, close the widget, or hide known selectors immediately before capture. Keep this behavior explicit so you do not accidentally remove content you need to document.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Full-page output misses images

Cause: images load only after entering the viewport. Fix: scroll through the document, wait for image completion, or use a capture service that loads lazy images as part of full-page rendering.

The element selector fails

Cause: the element is absent, hidden, duplicated, or inside an iframe. Fix: verify the selector in the same browser context, wait for visibility, select the intended match, and handle iframe content through its frame.

Navigation times out

Cause: slow servers, never-ending requests, bot checks, or a page that continuously polls. Fix: use a realistic timeout, wait for domcontentloaded plus a readiness selector, and log the final URL and response status. Do not blindly increase the timeout for every job.

Transparency is black or missing

Cause: JPEG has no alpha channel, or the page paints an opaque background. Fix: use PNG/WebP as appropriate and request omitBackground: true; verify that the browser-rendered page actually has transparent regions.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Performance, reliability, and cost considerations

Launching a browser for every URL is simple but expensive in CPU and startup time. For a worker that handles many jobs, reuse a browser process while creating an isolated context or page per job, cap concurrency, and close idle resources. Full-page and high-density images consume more memory than viewport PNGs. JPEG or WebP can reduce transfer size when lossless output is unnecessary.

Make jobs observable: record navigation duration, readiness wait, output dimensions, file size, and a categorized error. Retry transient network failures with a limit, but do not repeatedly retry deterministic selector or authentication errors. Treat third-party pages as untrusted input and apply your normal sandbox, outbound-network, and secret-handling policies.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

ScreenshotNeo provides a website screenshot API and MCP server. One GET request returns PNG, JPEG, WebP, or a PDF. It accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before the capture; each cleanup step can be switched off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed as clean shots, and response headers identify the page verdict and billing status.

Use the API from TypeScript’s surrounding toolchain or any server that can make HTTP requests:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`ScreenshotNeo returned ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await Bun.write('shot.webp', image);

See the complete options and response details in the ScreenshotNeo documentation. The same endpoint also supports full-page and element captures, dark mode, device presets, retina scale, PDF paper and page controls, custom CSS and JavaScript, clicks, selector waits, delays, network-idle waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, chosen cache TTLs, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification. Parameter names used by other screenshot APIs are accepted to ease migration.

An MCP server exposes take_screenshot, get_page_info, and capture_pdf to Claude, Cursor, and other MCP clients, so an AI agent can request captures without you maintaining browser automation.

Plans include 1,000 screenshots per month free with no card; paid plans start at $5 for 3,000 shots. Higher plans are Growth ($15/15,000), Pro ($39/60,000), Scale ($99/250,000), and Business ($249/1,000,000); yearly billing gives two months free, and every feature is on every plan. Create a free ScreenshotNeo account.

Frequently Asked Questions

Can I screenshot a page that requires JavaScript?

Yes. Playwright and Puppeteer execute the page in Chromium, so wait for the application’s rendered state rather than capturing immediately after the initial HTML response.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What does omitting the screenshot path return?

Both libraries return image bytes in memory, which you can upload to object storage or return from an HTTP endpoint instead of writing a local file.

Why is my full-page image extremely tall?

Full-page mode includes the entire scrollable document. Consider an element or clipped capture, or split a very long document into purposeful regions.

Is network idle enough for every site?

No. Polling, animations, consent dialogs, and client-side hydration can continue after network-idle detection. A page-specific selector or application event is usually more reliable.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.