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.

To capture lazy-loaded content with a Node.js screenshot, first make the page load that content: scroll through it (or scroll its specific container), wait for a page-specific readiness signal, then take the screenshot. In Playwright, fullPage: true captures the full scrollable page, but it does not guarantee that a site has loaded everything that appears only after scrolling. [Playwright Page API] [Playwright issue #40941]

Why a full-page screenshot can still have blank areas

Lazy loading defers work until it is needed. A page might fetch an image when it approaches the viewport, reveal a section after a scroll event, or append feed items as the visitor moves down the page. The browser’s Intersection Observer API, for example, lets a page observe when an element intersects a viewport or an ancestor element.

A full-page capture concerns the extent of the image, not the site’s readiness. Playwright describes a full-page screenshot as capturing the scrollable page as though it fit on a very tall screen. That does not necessarily produce the same events as a person scrolling through the page. A Playwright issue opened May 21, 2026, reports cases involving lazy images, IntersectionObserver-triggered content, scroll-triggered animations, and virtualized lists; treat it as a documented failure mode to account for, not a claim that every page or browser behaves identically. [Playwright Screenshots documentation] [Playwright issue #40941]

Three different things that may be called lazy loading

  • Deferred images or sections: elements load or appear when they approach the visible area.
  • Infinite scroll: reaching the bottom requests and appends more items, potentially without a natural end.
  • Virtualized lists: the page may render only nearby rows and replace them as the viewport moves. A full-page image cannot be assumed to include every item in such a list.

Decide whether you need a screenshot of one finite document or a particular slice of a changing feed. For an unbounded feed, define a stopping point such as a target item, known count, or maximum scroll depth.

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

Capture a lazy-loaded page with Playwright

This Node.js example scrolls by roughly one viewport at a time, allows asynchronous work to run, and stops when the document height remains unchanged or it reaches a safety limit. It then takes a full-page PNG. The scroll-and-wait loop is an adaptable implementation pattern, not a universal recipe: the right scroll target, pause, and completion condition depend on the site.

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

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

    await page.goto('https://example.com', {
      waitUntil: 'domcontentloaded'
    });

    let previousHeight = 0;
    for (let i = 0; i < 30; i++) {
      const height = await page.evaluate(
        () => document.documentElement.scrollHeight
      );
      if (height === previousHeight) break;
      previousHeight = height;
      await page.evaluate(() => window.scrollBy(0, window.innerHeight));
      await page.waitForTimeout(400);
    }

    await page.screenshot({ path: 'page.png', fullPage: true });
  } finally {
    await browser.close();
  }
})();

Install Playwright in a Node.js project with npm install playwright, then install its browser with npx playwright install chromium. Save the script as a JavaScript file and run it with node filename.js. The example uses Chromium and a 1280-by-900 viewport; change those to suit your capture. Playwright documents the page screenshot API and its fullPage option in its Page API.

Make the stopping condition match the page

The sample stops when the height stops growing. That works only when loading new content changes document height and the short pause is long enough for the site to respond. If a known element or expected item count signals completion, wait for that instead. For example, after scrolling, use await page.locator('[data-testid="last-item"]').waitFor() when that selector genuinely identifies the final expected item. A fixed delay alone cannot establish that all content has arrived.

For a finite page whose content is already present but images load lazily, visiting successive scroll positions can trigger those image requests. You may also wait for a page-specific image condition before capture, such as the expected images being complete. Avoid treating network quiet as a universal signal: some pages maintain ongoing requests, and quiet network traffic does not necessarily mean the exact content you need is rendered.

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

Use the right scrolling target

The example scrolls the window. If the article or feed sits inside a nested scrolling region, scroll that element instead of window. You can locate it with a CSS selector and advance its scrollTop, or bring a target element into view with Playwright’s locator methods. If the page reacts to actual user-like movement, scroll incrementally and allow the page to process each step rather than jumping straight to the bottom.

For infinite scroll, the height may keep increasing on every iteration. The 30-iteration guard prevents an endless loop but does not mean the feed is complete. Replace it with a meaningful cap or condition for your task. If the page must remain interactive after the capture, record and restore the original scroll position.

Capture a specific element or use Puppeteer

Element screenshots

If the desired content is one card, chart, or article region, capture that element rather than the whole document. Playwright supports locator screenshots, while Puppeteer’s official guide documents both Page.screenshot() and ElementHandle.screenshot(). The element still needs to be loaded and visible as required by the page before capture. [Playwright Page API] [Puppeteer Screenshots guide]

await page.locator('.report-card').screenshot({ path: 'report-card.png' });

When Puppeteer is already in your project

Puppeteer is also a reasonable Node.js choice for page and element screenshots. The same underlying strategy applies: navigate, trigger the target page’s loading behavior, wait for a meaningful condition, and then call its screenshot API. The cited documentation establishes screenshot capabilities, not a universal winner for speed, fidelity, or reliability; choose based on your existing project and browser requirements. [Puppeteer Screenshots guide]

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

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server by Yorker Media. Send a GET request with the page URL for a PNG, JPEG, WebP, or PDF. For a direct Node.js request, replace the target URL and set your API key:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo API documentation for request options. Before capture, it accepts cookie or consent banners like a visitor and removes 60-plus known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 shots a month without a card; paid plans start at $5 for 3,000 shots.

Sign up for 1,000 free screenshots a month, with no card required.

Troubleshooting missing content and failed captures

  • Blank space below the fold: The page may not have received scroll events before capture. Scroll through it in increments, then wait for the relevant section or item count rather than relying on fullPage alone.
  • The page height never grows: Content may be inserted without changing the height, or the page may use a nested scrolling container. Inspect which element scrolls and wait for a page-specific signal.
  • The loop never finishes: An infinite feed can keep adding content. Use an explicit iteration, item, or depth limit tied to the capture you want.
  • Some images are still missing: Scrolling may have triggered the request, but not completed it. Wait for the expected images or another site-specific readiness condition, then inspect the resulting PNG.
  • Capture ends before an animation or delayed section appears: Add a condition for the final state, or a bounded wait after triggering it. A delay is a fallback, not proof that every asynchronous operation finished.
  • Nothing happens when scrolling the window: The content may be inside an element with its own scrollbar. Scroll that element or bring the relevant target into view.
  • The process runs indefinitely on a live page: Keep a maximum-iteration guard and ensure the browser is closed in a finally block, as in the example, so an error does not leave it running.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Improve repeatability, runtime, and output checks

Use the smallest capture scope that meets the requirement: an element screenshot can avoid processing an entire long page. For full-page captures, set a deliberate viewport and cap scrolling, especially on dynamic feeds. A wider or taller viewport can alter responsive layout and which elements approach the viewport, so keep it consistent when comparing captures.

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

Prefer explicit page signals—an expected element, count, or state—over increasing arbitrary sleeps. Keep a maximum wait and scrolling limit to bound runtime when the site is slow or continually changes. After capture, inspect the output for missing sections, placeholder image areas, duplicated feed items, or content cut off by virtualization. If repeatability matters, capture the same URL with the same viewport and stopping condition; the page itself may still change between visits.

Playwright and Puppeteer both expose page screenshot APIs, and Playwright also documents full-page capture. Available documentation does not establish which library is categorically faster or more reliable for every site, so the site’s scroll behavior and the needs of your existing Node.js project should decide. [Playwright Page API] [Puppeteer Screenshots guide]

Frequently Asked Questions

Does Playwright’s fullPage: true trigger lazy loading?

It sets the screenshot extent; it should not be treated as a guarantee that the page has run all scroll-triggered loading behavior.

Can one screenshot include every item in an infinite feed?

Not reliably as an unbounded feed. Set a specific endpoint or capture limit, and account for pages that render only nearby virtualized rows.

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

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.