ScreenshotNeo

BlogHow-to

How to Take Dynamic Screenshots of Websites

Capture websites after JavaScript, scrolling, and interactions reveal the content you need. Learn viewport, full-page, and element screenshots with Playwright and Puppeteer.

By the ScreenshotNeo team4 October 20268 min read

To take a dynamic website screenshot, first reproduce the page state you need, wait for that state to appear, and then capture the viewport, the full scrollable page, or a specific element. JavaScript rendering, lazy loading, scrolling, and interaction can all affect what is visible. A full-page option captures a taller document; it does not guarantee that content which loads only after scrolling or clicking has appeared.

For repeatable captures, Playwright and Puppeteer provide screenshot APIs. Choose based on the automation setup and options your project needs; the available documentation does not establish a universal speed or reliability winner. Playwright screenshots · Puppeteer screenshots.

1. Decide what the screenshot should show

Choose the capture target before writing code:

  • Viewport: the part currently visible in the browser window.
  • Full page: the full scrollable document, as if it fit on a very tall screen. Playwright documents this mode in its screenshot guide.
  • Element: one component such as a chart, header, card, or form.

Then identify the page state to capture. If the needed content lives behind a tab, accordion, menu, or other interaction, perform that interaction and wait for its result first. If content appears as the page scrolls, plan to scroll through the relevant region and check that it loaded before capture.

2. Take a one-off screenshot manually

For a single image, a browser’s built-in screenshot or developer tools may be enough. The exact controls depend on the browser and version. Use a full-page option when available, or capture the visible viewport. For a particular component, inspect or select that element with the browser’s developer tools, or use an automation library if the manual workflow cannot target it cleanly.

Manual capture is convenient for an occasional image, but it is harder to reproduce the same viewport, wait condition, and interaction sequence across many pages or repeated runs.

3. Capture dynamic pages with Playwright

Install Playwright for Node.js and its Chromium browser:

npm install playwright
npx playwright install chromium

Save this as capture.mjs. It waits for a meaningful page element, then writes a full-page PNG. Replace the example URL and main selector with values appropriate for the site.

import { chromium } from 'playwright';

const browser = await chromium.launch({ headless: true });
try {
  const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
  await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
  await page.locator('main').waitFor({ state: 'visible', timeout: 15000 });
  await page.screenshot({ path: 'page.png', fullPage: true });
} finally {
  await browser.close();
}

main is only an example readiness condition: its appearance does not prove that every image, request, or client-side update is finished. Wait for the specific content your image must contain, such as a chart title or a result row. Playwright’s screenshot guide also shows viewport capture, full-page capture, and element screenshots.

Capture an element

const chart = page.locator('#revenue-chart');
await chart.waitFor({ state: 'visible' });
await chart.screenshot({ path: 'chart.png' });

Use a selector that uniquely identifies the intended component. If the element is below the fold, ensure it is visible and populated before capture.

Load scroll-triggered content

Some pages load images or sections only when they approach the viewport. Scroll in steps through the required area and wait for a page-specific signal after each step. The following helper is deliberately generic; adapt its wait condition to the site rather than assuming a fixed delay is sufficient.

const height = await page.evaluate(() => document.body.scrollHeight);
for (let y = 0; y < height; y += 700) {
  await page.evaluate((scrollY) => window.scrollTo(0, scrollY), y);
  await page.waitForTimeout(250);
}
await page.evaluate(() => window.scrollTo(0, 0));
await page.screenshot({ path: 'page.png', fullPage: true });

This is a practical way to prompt scroll-dependent loading, not a guarantee that every site has finished loading. Pages can extend their height as new content appears; for those pages, recheck the document height as you scroll and stop when the target content is present or a clear end condition is reached. Inspect the screenshot output.

Make captures more repeatable

Dynamic timestamps, animations, or rotating elements can make repeated screenshots differ. Playwright accepts a stylesheet for a screenshot, which can hide or change such elements. Use that only when a stable comparison is the goal, and record the visual changes so the result is not mistaken for the unmodified page.

await page.screenshot({
  path: 'stable-page.png',
  fullPage: true,
  style: `
    *, *::before, *::after {
      animation: none !important;
      transition: none !important;
    }
    .live-timestamp { visibility: hidden !important; }
  `,
});

See the Playwright page screenshot API for current screenshot options.

4. Capture dynamic pages with Puppeteer

Install Puppeteer, which downloads a compatible browser as part of its standard installation:

npm install puppeteer

Save this as capture-puppeteer.mjs to capture a full page after its main content appears:

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch({ headless: true });
try {
  const page = await browser.newPage();
  await page.setViewport({ width: 1440, height: 900 });
  await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
  await page.waitForSelector('main', { visible: true, timeout: 15000 });
  await page.screenshot({ path: 'page.png', fullPage: true });
} finally {
  await browser.close();
}

Puppeteer’s screenshot guide uses networkidle2 as an example navigation wait:

await page.goto('https://news.ycombinator.com', {
  waitUntil: 'networkidle2',
});
await page.screenshot({ path: 'hn.png' });

networkidle2 is not a universal “everything is ready” signal. Sites with ongoing requests, polling, or later client-side updates may never reach the state you expect—or may become quiet before the target content appears. Prefer a selector or other condition tied to the content you need, and use the navigation wait as appropriate. Puppeteer also supports element screenshots; its element screenshot flow scrolls a hidden element into view. See its ScreenshotOptions API for documented options.

5. Choose the capture options

Need Approach Watch for
Visible browser area Take a normal page screenshot after the target state is ready. The output is limited to the viewport.
Whole document Set fullPage: true in Playwright or Puppeteer. Full-page height does not itself trigger every lazy load or interaction.
One component Use a Playwright locator screenshot or Puppeteer element screenshot. Wait for the element to be visible and populated.
Only a region Use a clipping rectangle in Puppeteer, or target a specific element. Check coordinates and viewport size; clipping can exclude content.
Stable visual comparison In Playwright, apply a screenshot stylesheet to suppress known changes. Styling can alter what the page represents.

Puppeteer documents options including full-page capture, clipping, image type, quality, and omitting the background. Quality applies to supported lossy image output; consult the current option reference for exact behavior. Playwright can return screenshot bytes for further image processing as well as writing to a path; see its API reference.

6. Common problems and fixes

Symptom Likely cause Fix
Text, chart, or image is missing Capture ran before the relevant JavaScript or asset finished. Wait for a specific visible element or loaded-state signal; verify the resulting image.
Lower sections are blank Content or images load on scroll. Scroll through the target region in steps, allow loading, and check the final output.
Content appears only after clicking The page state was never activated. Click or otherwise reproduce the state, then wait for its resulting content before capture.
Navigation hangs waiting for network idle The page keeps requests open or makes ongoing requests. Use a different navigation condition and wait for the specific content needed instead.
Repeated images differ Animation, timestamps, rotating content, or live data changes. Wait for the desired state; where appropriate, use Playwright screenshot styling to suppress known motion or volatile elements.
The image is unexpectedly large or cut off Full-page capture includes a long document, or a clip/viewport is misconfigured. Capture a specific element or region, or adjust the viewport and clipping bounds.
Selector wait times out The selector is wrong, not unique, hidden, or absent in that page state. Inspect the rendered page, correct the selector, and wait for the state that reveals it.

7. Performance, reliability, and cost

Browser automation requires starting a browser, loading the page, reaching the desired state, and producing the image. For repeated jobs, reuse a browser process where appropriate, close pages when finished, and limit concurrent captures to what the host can support. Large full-page images take more memory and storage than a viewport or element capture.

Reliability depends on the wait condition and the site’s behavior. A fixed sleep can be too short on a slow run and waste time on a fast one. Prefer a condition tied to the expected content, add a finite timeout, and inspect or retry failures deliberately. Scroll-triggered content and third-party assets remain site-dependent.

Playwright and Puppeteer are open-source browser automation libraries; the practical costs of self-managed capture include compute, browser maintenance, storage, and engineering time. No comparative benchmark or universal cost figure is established here. If screenshots are occasional, manual capture may be enough; if they are automated at scale, estimate browser runtime and infrastructure from your own workload.

8. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a screenshot or PDF, while its capture options include full-page capture with lazy images loaded, element capture, viewport and device presets, custom waits, and more. See the ScreenshotNeo API docs.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

Cookie banners are accepted and removed before the shot, along with known newsletter popups and chat widgets. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, no card required.

9. FAQ

Does full-page capture include content that appears after scrolling?

It captures the document’s full scrollable extent, but it does not guarantee that every scroll-triggered section or asset has loaded. Scroll through the relevant area and verify the image.

Should I use Playwright or Puppeteer?

Use the library that fits your existing project and needed options. The cited documentation establishes screenshot capabilities, not a universal winner.

Can I capture just a chart or card?

Yes. Both libraries support element screenshots. Wait until the target is visible and contains the state you want.

Is a network-idle wait enough?

Not always. It describes network activity, not whether the particular content you care about has rendered. A page-specific readiness condition is usually clearer.

References