ScreenshotNeo

BlogHow-to

How to Screenshot a Lazy-Loaded WordPress Page After Scrolling

Scroll through a WordPress page to trigger deferred images and content, then capture the full page with Playwright or a browser-based workflow.

By the ScreenshotNeo team4 October 20269 min read

To screenshot a lazy-loaded WordPress page with its below-the-fold images, scroll through the page in increments, wait for the content you need to load, and then take a full-page screenshot. A full-page option captures the document’s scrollable area, but it may not trigger scripts that load content only after actual scrolling or viewport intersection.

For a one-off image, scroll manually and use your browser’s available full-page capture feature. For repeatable captures, automate the scrolling and wait for the relevant images or sections before saving the screenshot. This guide shows a runnable Playwright example and explains how to adapt it when the page’s loading behavior is different.

1. Why a full-page screenshot can miss lazy-loaded content

“Below the fold” means outside the browser’s initial visible area. Lazy loading defers offscreen images or other content until they approach or enter the viewport. WordPress 5.5 introduced native HTML loading="lazy" for images by default, but themes and plugins can also use JavaScript or viewport observers to defer content.

A full-page screenshot expands capture to the scrollable page. That does not guarantee that the browser actually scrolled through every section before capturing. If a theme or plugin waits for a real scroll or an intersection with the viewport, content may remain unloaded in the screenshot unless the page was scrolled first. Google’s guidance describes loading relevant content when it becomes visible in the viewport; the same general behavior explains why scrolling can matter for a screenshot. Google Search Central: Lazy loading

WordPress’s default behavior is not a guarantee that every image or plugin uses the same mechanism. Inspect the particular page and wait for the content you need rather than assuming that one delay works for every site.

2. Manual workflow for a one-off capture

  1. Open the published page and wait for its initial content to settle.
  2. Scroll down in increments through the sections that should appear in the screenshot. Pause where images or other content need time to load.
  3. If a specific image or section is still missing, check that content directly. Scrolling past it does not prove that its request succeeded.
  4. Use the browser’s available full-page screenshot feature, if it has one, to capture the whole scrollable page. Menu names and availability vary, so consult the documentation for your browser and version.
  5. Inspect the saved image for blank images, unfinished sections, or repeated sticky headers.

If you only need the visible screen, take a viewport screenshot instead. If you need one card, image, or section, an element screenshot may be more suitable than the whole document.

3. Automate scrolling and capture with Playwright

The following Node.js script opens a page, scrolls through it in viewport-sized steps, waits for images to finish loading where possible, and writes a full-page PNG. Replace the URL and, when practical, set READY_SELECTOR to an element that identifies the content you need. The scroll gives viewport-triggered loading code a chance to run; the image checks help catch images that have not loaded successfully.

import { chromium } from 'playwright';

const url = 'https://example.com/long-wordpress-page/';
const readySelector = null; // Example: '.article-body .last-section'
const outputPath = 'wordpress-page.png';

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

try {
  await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60_000 });

  // Optional: wait for a page-specific landmark before scrolling.
  if (readySelector) {
    await page.locator(readySelector).waitFor({ state: 'attached', timeout: 30_000 });
  }

  // Scroll the real page viewport so intersection and scroll listeners can run.
  let previousHeight = 0;
  let unchangedHeightRounds = 0;
  for (let round = 0; round < 30; round++) {
    const state = await page.evaluate(() => {
      const step = Math.max(400, Math.floor(window.innerHeight * 0.8));
      window.scrollBy(0, step);
      return { y: window.scrollY, height: document.documentElement.scrollHeight };
    });

    await page.waitForTimeout(300);
    unchangedHeightRounds = state.height === previousHeight
      ? unchangedHeightRounds + 1
      : 0;
    previousHeight = state.height;

    const atBottom = await page.evaluate(() =>
      window.scrollY + window.innerHeight >= document.documentElement.scrollHeight - 2
    );
    if (atBottom && unchangedHeightRounds >= 2) break;
  }

  // Give the final visible content a chance to settle, then return to the top.
  await page.waitForTimeout(500);
  await page.evaluate(() => window.scrollTo(0, 0));

  // Wait for images that have a source to finish. Broken images are reported below.
  await page.waitForFunction(() => {
    const images = [...document.images].filter((img) => img.currentSrc || img.src);
    return images.every((img) => img.complete);
  }, { timeout: 30_000 }).catch(() => {});

  const imageReport = await page.evaluate(() =>
    [...document.images].map((img) => ({
      src: img.currentSrc || img.src,
      loaded: img.complete && img.naturalWidth > 0,
      width: img.naturalWidth
    }))
  );
  const failed = imageReport.filter((img) => !img.loaded);
  if (failed.length) console.warn('Images not loaded:', failed);

  await page.screenshot({ path: outputPath, fullPage: true });
  console.log(`Saved ${outputPath}; checked ${imageReport.length} images.`);
} finally {
  await browser.close();
}

Save it as capture.mjs, then install Playwright and its browser once with npm install playwright and npx playwright install chromium. Run it with node capture.mjs. Playwright documents fullPage screenshots as capturing the full scrollable page; its default is false. Playwright screenshots

The loop uses an 80% viewport step to overlap sections and stops after reaching the bottom with a stable document height. These are practical defaults, not universal requirements. Pages with infinite scroll, delayed inserts, or continuously changing height may need a known stopping selector, a maximum scroll position, or a site-specific readiness condition. The 300 ms and 500 ms waits are brief opportunities for the page to respond, not guarantees that any particular site has finished.

4. Make the automation fit the page

Wait for a known image or section

A page-wide networkidle wait can be unreliable on sites with analytics, polling, or other ongoing requests. Prefer a condition tied to the content you need. For an image, you can wait until it is complete and has nonzero natural width:

await page.waitForFunction(() => {
  const img = document.querySelector('.article-body img.featured');
  return img && img.complete && img.naturalWidth > 0;
}, { timeout: 30_000 });

For a section, wait for a meaningful selector and, if its contents are inserted asynchronously, check for the expected child or text. A selector appearing in the DOM does not necessarily mean its images have loaded.

Handle pages that add content as you scroll

Some pages append another batch of posts near the bottom. Keep scrolling until the expected final item appears or the document height stops growing, with a maximum number of rounds to avoid an endless loop. The sample has a 30-round cap for this reason. If the page is intentionally infinite, define the exact portion to capture rather than trying to reach a bottom that does not exist.

Capture the right area

  • Viewport: capture only what is currently visible.
  • Full page: capture the scrollable document after triggering deferred content.
  • Element: capture a particular component, such as an article body or product card, after it is ready.

Playwright supports full-page and element screenshots. Its documented full-page option does not replace the scrolling and readiness steps when a page depends on viewport activity. Playwright screenshot options

5. Troubleshooting missing images and incomplete captures

Symptom Likely cause What to try
Images below the fold are blank The page’s lazy loader has not seen them enter the viewport, or their requests failed. Scroll through the relevant sections before capture. Check the image’s currentSrc, complete, and naturalWidth; wait for the specific image when possible.
The screenshot ends before the last content Content was appended after the scroll loop reached its earlier bottom, or the page height changed. Recheck the height after pauses, continue scrolling until the expected last section appears, and retain a round limit for infinite-scroll pages.
The script times out during navigation The site is slow, a request hangs, or the selected navigation condition never occurs. Use a suitable navigation event such as domcontentloaded, increase the navigation timeout if justified, and wait separately for the content selector you actually need.
The readiness selector never appears The selector does not match this page, appears only after another action, or the page failed to render that section. Inspect the published page’s DOM, confirm the selector, and distinguish an attached element from one that is visible and populated.
Some images remain broken after scrolling The image URL may return an error, require authentication, be blocked, or be replaced by a responsive source. Check currentSrc and the browser’s network errors. Confirm access and use the actual image or section condition; extra waiting cannot fix a failed request.
Sticky navigation repeats in the full-page image The site’s sticky or fixed element is rendered during full-page capture in a browser-dependent way. Inspect the output. If appropriate for your use, hide that selector with page CSS before capture; verify the result because page-specific layout behavior varies.
Capture is very tall or memory-heavy The page is unusually long, contains many loaded images, or keeps adding content. Limit the capture to the needed section or page range, or capture sections separately. Avoid unbounded scrolling.

6. Performance, reliability, and cost

Scrolling and waiting add time, but they prevent a quick capture from silently omitting content. Keep the viewport and scroll steps large enough to make progress while preserving overlap; use a page-specific readiness check to avoid a long fixed sleep. There is no single reliable delay for all WordPress themes, plugins, network conditions, and browser engines.

For repeatable jobs, set navigation and selector timeouts, cap the number of scroll iterations, report images whose natural width is zero, and save only after the expected section is ready. If a screenshot matters, inspect or validate the output rather than treating a successful screenshot call as proof that every asset loaded. Full-page images can consume substantial memory on very long pages; an element or section capture can be more practical.

With a self-hosted browser, account for the browser runtime and infrastructure you operate. A screenshot API can avoid maintaining browser setup, but compare its behavior and billing rules against your needs.

7. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF. For a WordPress page, request a full-page capture so the result includes the scrollable document; whether a particular site’s deferred content is ready can still depend on its loading behavior. See the ScreenshotNeo API documentation for options, including full-page capture and wait conditions.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://example.com/long-wordpress-page/ \
  -d full_page=true \
  -o wordpress-page.webp

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={
        "access_key": "YOUR_API_KEY",
        "url": "https://example.com/long-wordpress-page/",
        "full_page": "true",
    },
    timeout=90,
)
r.raise_for_status()
with open("wordpress-page.webp", "wb") as image:
    image.write(r.content)

Node.js

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com/long-wordpress-page/',
  full_page: 'true',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(({ writeFile }) =>
  writeFile('wordpress-page.webp', image)
);

The API also supports waiting for a selector, a delay, or network idle; custom CSS and JavaScript; and PNG, JPEG, WebP, or PDF output. To help keep a screenshot clean, it accepts cookie or consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed. Responses identify the page verdict and billing status in headers. 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 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is on every plan, and yearly billing gives two months free. For long pages, choose a full-page capture and add an appropriate wait condition for the content that must appear. Sign up for 1,000 free screenshots a month, with no card.

8. FAQ

Does WordPress lazy-load every image?

WordPress 5.5 introduced native lazy loading for images by default. A theme or plugin may add its own behavior, and individual page markup can vary.

Will taking a full-page screenshot scroll the page for me?

Do not assume so. Full-page capture describes the area captured; scroll through the page first when its content depends on viewport entry or scroll events.

How long should I wait after scrolling?

There is no universal duration. Wait for the image or section you need to be ready, and use a timeout as a failure bound rather than as proof of readiness.

Can I capture only the article instead of the whole page?

Yes. Use an element screenshot when the article body is the only content you need. Scroll through it first if its images load as it enters the viewport.

Sources