ScreenshotNeo

BlogAI agents

How to Make an AI Agent Screenshot a Webpage With a Sticky Header Included

Use Playwright to capture a full page, preserve a sticky header’s visible state, and check the result. Includes runnable agent code and a no-browser-setup option.

By the ScreenshotNeo team4 October 20267 min read

To capture a webpage’s full scrollable document with Playwright, take a full-page screenshot with fullPage: true. If you also need to preserve the sticky header exactly as it appears in the viewport, capture the header element separately: a full-page image and a header-element image answer different questions. Playwright documents full-page capture as covering the full scrollable page, but it does not guarantee how every site’s sticky header will appear in that image. Playwright’s screenshot guide shows the full-page option.

Choose what the agent should capture

Decide whether the deliverable needs the whole document, the current visible screen, or a separate record of the header’s visible state:

Capture What it shows Use it when
Viewport The current visible browser area, including the sticky header if it is visible at that moment. You need to record what a visitor currently sees.
Full page The full scrollable document in one tall image. You need a page overview or full-document record.
Header element A crop bounded by the header element. You need an explicit image of the header as it appears at a chosen state.

For a deliverable that must contain both the document and the visible header, save two images: one full-page image and one header crop. That makes the header’s intended state explicit without relying on how a particular browser renders a sticky element while stitching or laying out a tall capture.

Capture a full page and sticky header with Playwright

Install Playwright and its Chromium browser, then save this as screenshot.mjs. It navigates to a page, scrolls through it to trigger common scroll-based lazy loading, returns to the top, and saves both the full document and the header crop.

npm install playwright
npx playwright install chromium
import { chromium } from 'playwright';

const url = process.argv[2] ?? 'https://example.com';
const headerSelector = process.env.HEADER_SELECTOR ?? 'header';
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({
  viewport: { width: 1440, height: 900 },
  deviceScaleFactor: 1,
});

try {
  await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30_000 });
  await page.locator(headerSelector).waitFor({ state: 'visible', timeout: 15_000 });

  // Scroll in viewport-sized steps so scroll-triggered content can load.
  await page.evaluate(async () => {
    const step = Math.max(400, window.innerHeight - 100);
    for (let y = 0; y < document.documentElement.scrollHeight; y += step) {
      window.scrollTo(0, y);
      await new Promise(resolve => setTimeout(resolve, 150));
    }
    window.scrollTo(0, 0);
  });
  await page.waitForTimeout(300);

  await page.screenshot({ path: 'page.png', fullPage: true });
  await page.locator(headerSelector).screenshot({ path: 'header.png' });
  console.log('Saved page.png and header.png');
} finally {
  await browser.close();
}

Run it with node screenshot.mjs https://example.com. If the site uses a different header selector, set it explicitly, for example HEADER_SELECTOR='[data-testid="site-header"]' node screenshot.mjs https://example.com. The selector must identify the actual header element. A generic header may match multiple elements or a non-sticky section on some pages.

The header crop is taken after scrolling back to the top. If the header changes style after scrolling—for example, it shrinks or gains a background—move to the scroll position where that state appears, wait for the change, and then capture the locator. Locator screenshots capture the element bounds; they do not promise identical sticky behavior across all full-page captures. See the element screenshot API.

Use Playwright MCP from an AI agent

When the agent operates through Playwright MCP, use its screenshot tool to capture the full scrollable page. Use a second call targeting the header selector if the agent needs a distinct header image. The MCP screenshot options include viewport, element, and full-page capture; its documentation says full-page capture cannot be combined with an element target. The scale option selects CSS-pixel or device-pixel output. Consult the Playwright MCP documentation for the screenshot tool’s current argument schema.

Give the agent a concrete instruction such as: “Open the URL at 1440 × 900, wait for the header selector header, scroll through the page to load scroll-triggered content, return to the top, save a full-page screenshot, then save a separate screenshot of header. Report both output paths and whether you inspected the images.” A useful agent reports what it actually did; it should not claim the image is correct unless it inspected the result.

Make the capture more reliable

  1. Use a stable viewport and browser. Keep viewport dimensions, browser version, device scale, and execution environment consistent when comparing images.
  2. Wait for the page state you need. domcontentloaded means the initial document was parsed; it does not mean every image, font, or client-rendered section is ready. Wait for a specific selector or page condition when the site provides one.
  3. Trigger lazy content deliberately. A full-page option describes the document extent; it does not guarantee that scrolling activated every page-specific lazy loader. Scroll in steps, wait for content, and check the resulting image for gaps.
  4. Capture the desired header state explicitly. If a sticky header changes after scrolling, capture its element at that scroll position. Capture the full page separately.
  5. Inspect the output. Check that the header, lower sections, and expected overlays or their absence are visible. API options alone cannot prove a particular site rendered as intended.

For visual regression work, Playwright Test’s toHaveScreenshot() waits for two consecutive screenshots to match before comparing with the stored expectation. Screenshot assertions also support controls for animation handling, masks, clip regions, and injected styles. These can reduce noise, but rendering may still vary with operating system, browser version, hardware, settings, power source, and headless mode. Keep the environment consistent; see Playwright visual comparisons and the PageAssertions API.

Common sticky-header screenshot problems

Symptom Likely cause Fix
Header is missing from the tall image The full-page rendering does not preserve the site’s sticky behavior as expected, or the header is hidden in the page’s current state. Save a separate locator screenshot at the desired scroll position and inspect both images.
Header appears repeated or in an unexpected position The page’s sticky or fixed positioning interacts with full-page rendering in a browser- or site-specific way. Keep the full-page image for document coverage and capture the visible header separately. Check the actual output instead of assuming a universal result.
Lower content is blank or incomplete Content loads only after scrolling, or the capture began before client-side rendering finished. Scroll through the page in steps, wait for expected content or selectors, then capture. Increase waits only where the page needs them.
Header selector times out The selector does not match, the header is inside a frame, or it has not appeared yet. Inspect the page markup, use a stable selector, and wait for the correct frame or application state.
Images differ between runs Fonts, animations, dynamic content, browser versions, or host conditions differ. Use the same browser and viewport, wait for stable content, and use screenshot assertion controls such as animation handling or masks where appropriate.
Element capture fails The target is absent, hidden, or not a single suitable visible element. Confirm the selector resolves to the intended visible element and capture at the state where it is displayed. Element capture uses the element’s bounds and has visibility requirements.

Performance, reliability, and cost

A full-page capture of a long document produces a tall image and can use more memory and time than a viewport screenshot. Scrolling to trigger lazy content adds navigation time, and taking a separate header crop adds another capture. Use a viewport image when you only need the current screen; reserve full-page capture for cases where the whole document matters. Set navigation and selector timeouts deliberately, close the browser in a finally block, and avoid parallel captures against the same page when they could change scroll position or state.

Browser automation shifts the work to your runtime: you manage browser installation, execution, and capture failures. For repeatable comparisons, control the browser and host environment. The reviewed Playwright documentation provides no universal promise for sticky-header output across sites, so treat inspection as part of a reliable workflow.

Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. Its API returns a screenshot or PDF from one GET request, and its MCP tools let AI agents call take_screenshot, get_page_info, and capture_pdf. For a standard full-page capture, use the API call below; see the ScreenshotNeo API documentation for options, including element capture and viewport settings.

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}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before capture; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card.

FAQ

Does fullPage: true guarantee the sticky header appears once at the top?

No. It requests a capture of the full scrollable page. Sticky behavior can vary with the site and rendering, so capture the header separately when its visible state matters.

Can I combine a full-page screenshot with a header selector in Playwright MCP?

No. The Playwright MCP screenshot documentation says its full-page option cannot be combined with an element target. Make separate captures.

Should the header be part of the full-page image or a separate file?

That depends on the deliverable. Use the full-page image for document coverage and a header crop when you need to preserve a specific visible header state.

Does a full-page capture load every lazy image?

Not necessarily. Scroll-triggered content may require scrolling and waiting before capture.