ScreenshotNeo

BlogHow-to

How to Capture a Website Screenshot Without Clipping a Fixed Header

Choose full-page, header-only, or clipped capture based on what you need. Here are runnable Playwright examples and fixes for headers that still appear cut off.

By the ScreenshotNeo team4 October 20269 min read

To avoid clipping a fixed header, choose a capture boundary that includes it: use a full-page screenshot for the whole document, a locator screenshot for the header alone, or a clip rectangle whose x, y, width, and height cover the complete header. Then inspect the saved image at its top and bottom edges. A screenshot API can capture the requested area, but no capture mode guarantees that every site’s fixed or sticky header will render as expected.

In Playwright, the shortest full-page fix is await page.screenshot({ path: 'page.png', fullPage: true }). For just the header, use await page.locator('header').screenshot({ path: 'header.png' }). For a custom crop, set the clip rectangle to include the header’s full bounds.

1. Choose the capture mode that matches the result

What you need Use What to watch for
The full scrollable document fullPage: true Very long pages can produce large image files. Inspect whether the header appears once or in unexpected positions.
Only the header A locator screenshot of the header element The selector must match the actual header. Overlapping elements can obscure it.
A specific viewport region A clip rectangle Coordinates and dimensions must cover the complete header, with room for borders or shadows if needed.
A repeatable fixed viewport A viewport screenshot, with a deliberate viewport size The default viewport may be too small or differ from the page’s target layout.

Playwright defines full-page screenshots as captures of the full scrollable page. Its API also supports a clip rectangle with top-left x and y coordinates, plus width and height. See the Playwright screenshot guide and Page screenshot API.

2. Set up Playwright

The examples use Node.js and Playwright. From an empty directory, install the package and browser:

npm init -y
npm install playwright
npx playwright install chromium

Save the following as capture.mjs. It captures a full page by default. Set MODE to header or clip to use the other methods.

import { chromium } from 'playwright';

const url = process.env.URL ?? 'https://example.com';
const mode = process.env.MODE ?? 'full';
const browser = await chromium.launch({ headless: true });

try {
  const page = await browser.newPage({
    viewport: { width: 1440, height: 1000 },
    deviceScaleFactor: 1,
  });

  await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });

  // Replace this selector with one that matches the site's header.
  const header = page.locator('header').first();
  await header.waitFor({ state: 'visible', timeout: 10000 });

  if (mode === 'header') {
    await header.screenshot({ path: 'header.png', animations: 'disabled' });
  } else if (mode === 'clip') {
    // Example viewport crop: x, y, width, and height are CSS pixels.
    // Adjust after checking the actual header bounds.
    await page.screenshot({
      path: 'clip.png',
      clip: { x: 0, y: 0, width: 1440, height: 120 },
      scale: 'css',
      animations: 'disabled',
    });
  } else {
    await page.screenshot({
      path: 'full-page.png',
      fullPage: true,
      scale: 'css',
      animations: 'disabled',
    });
  }
} finally {
  await browser.close();
}

Run it with:

URL=https://example.com MODE=full node capture.mjs
URL=https://example.com MODE=header node capture.mjs
URL=https://example.com MODE=clip node capture.mjs

The selector header is an example, not a universal selector. Sites may use .site-header, #masthead, or a framework-specific element. Inspect the page markup and replace it with a selector for the element you want.

3. Capture the whole page

Use full-page mode when the result should include content below the initial viewport. Playwright’s official guide describes this as capturing the full scrollable page. This removes the viewport’s bottom boundary as a constraint, but a site can still have its own layout or sticky-header behavior that affects the rendered image.

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

Keep fullPage as the only boundary mode in that call. In Playwright’s screenshot tool, a full-page capture cannot be combined with a target element. For a header-only result, use the element capture instead.

4. Capture only the header

Use a locator screenshot when the deliverable is the header itself rather than the page around it:

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

Replace .header with a selector from the page. If the site has several matching headers, narrow the selector or choose the first visible match after confirming that it is the intended element. A locator screenshot captures the element’s bounds; content overlapping it may affect what is visible. The Locator screenshot API documents the locator method.

5. Set a clip rectangle that includes the header

Use clip when you need a specific region of the viewport. Its values are the top-left x/y position and width/height. Those numbers are in CSS pixels for the screenshot geometry. A rectangle starting below the header, or ending before its bottom edge, will cut it off by definition.

await page.screenshot({
  path: 'header-and-content.png',
  clip: { x: 0, y: 0, width: 1440, height: 900 },
});

To measure the header rather than guess, inspect its bounding box:

const box = await page.locator('header').first().boundingBox();
if (!box) throw new Error('Header is not visible or has no bounding box');
console.log(box);

Use the measured bounds to check whether the crop begins at or above the header’s top and ends at or below its bottom. Add a few pixels when you want to preserve a border or shadow. A clip is a crop of the rendered page; it does not reposition a header that is covered by another element or hidden by the page’s CSS.

6. Handle fixed and sticky header behavior

position: fixed elements are positioned relative to the viewport; position: sticky elements change position as scrolling reaches their threshold. A full-page screenshot extends the captured document area, but the exact treatment of a fixed or sticky element can depend on how the page renders during capture. The official capture options define the boundary and geometry; they do not promise identical behavior for every page.

  1. Load the page at the viewport size you need, then wait for the header to be visible.
  2. Capture with the intended mode: full page, element, or clip.
  3. Open the resulting file and inspect the header’s top edge, bottom edge, text, and any content that should appear behind or below it.
  4. If a full-page result has an unexpected header, use a header locator for a header-only asset, or capture a deliberate viewport/clip region for a specific state.
  5. If the site changes its header after scrolling, reproduce the desired scroll state before capturing and verify the output. Do not assume the initial and scrolled states are interchangeable.

For a header-only capture, you can also scroll the element into view before taking the screenshot. Locator screenshots perform actionability checks and scroll the target into view as needed. For a full-page screenshot, avoid adding scrolling logic unless you specifically need a scrolled state: scrolling can change sticky-header positioning.

7. Screenshot options that affect the result

Option When it helps Notes
fullPage Capture the full scrollable page Use true; do not combine with a target in the Playwright screenshot tool.
clip Capture a defined viewport rectangle Set x, y, width, and height so the entire header fits inside.
scale: 'css' Keep one output pixel per CSS pixel Usually produces smaller images than device-pixel scaling on high-density displays.
scale: 'device' Preserve device-pixel resolution Can produce larger output dimensions and files.
animations: 'disabled' Reduce motion-related variation Finite animations are fast-forwarded; infinite animations are canceled for the capture.
timeout Set a screenshot operation limit Playwright’s screenshot timeout defaults to 0; configure a finite limit for bounded jobs.
type, quality Choose format or lossy compression PNG is the default. Quality applies to JPEG/WebP, not PNG.
style Apply temporary CSS during capture Useful for hiding dynamic elements, but CSS changes can alter the header; review the image afterward.

See the official screenshot API reference for option details and version availability. In particular, scale distinguishes CSS-pixel output from device-pixel output; a higher pixel count does not fix a crop that excludes the header.

8. Troubleshooting

Symptom Likely cause Fix
Top of header is missing The clip’s y starts below the header, or the capture is of a scrolled state. Set the clip’s y-coordinate to include the header top; capture the intended scroll state and inspect the output.
Bottom edge or shadow is cut The clip height ends at the header boundary or excludes its shadow. Increase the clip height slightly and check the saved image.
Header is absent in full-page output The page’s fixed/sticky behavior changed during full-page rendering, or the header is hidden in that state. Capture the header element separately or use a viewport/clip capture for the desired state. Verify the actual file.
Wrong element captured The selector matches a different or multiple elements. Inspect the DOM and use a more specific locator; verify visibility and bounding box.
Locator wait times out The selector is wrong, the header is delayed, or navigation did not reach the expected page state. Check the URL and selector, wait for the relevant element, and use a suitable navigation readiness condition.
Screenshot is inconsistent between runs Animations, late-loading content, fonts, or responsive layout may shift geometry. Set a stable viewport, wait for the header, disable animations when appropriate, and repeat visual inspection.
Screenshot appears blurry or unexpectedly large Device-pixel scaling changes output dimensions. Choose scale: 'css' for CSS-pixel output or retain device when high-density pixels are required.
Clip operation fails The rectangle has invalid dimensions or falls outside the usable page area. Check that width and height are positive and coordinates describe the intended visible region.

9. Performance, reliability, and cost

For a single image, a header locator capture generally limits the captured area; full-page output can be much taller and create larger files. CSS-pixel scale can keep files smaller on high-density displays. Choose the smallest capture boundary that still meets the deliverable, and avoid repeatedly capturing a long page when only its header is needed.

Reliability comes from controlling inputs and checking output: use a consistent viewport, wait for the target header, choose an intentional page state, and inspect the resulting image. A successful screenshot call only means the browser produced an image; it does not establish that the crop contains the intended header. Playwright itself has no per-screenshot API charge described in the cited documentation; infrastructure, browser runtime, and storage costs depend on where you run and retain captures.

10. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF. Its clean-shot flow accepts cookie and consent banners like a visitor and removes 60+ known consent platforms, newsletter popups, and chat widgets before capture; each of those steps can be turned off. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for AI agents.

For a page screenshot, use the API call below. The API supports full-page capture and element selection; see the ScreenshotNeo documentation for its request parameters. A remote screenshot API cannot guarantee a particular site’s fixed-header rendering, so inspect the returned image when exact framing matters.

cURL

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

Python

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)

Node.js

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, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. AI agents can take screenshots through the MCP server. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Every feature is on every plan. Create a free ScreenshotNeo account to get started.

11. FAQ

Should I use full-page capture for every fixed-header problem?

No. Use it when you need the entire scrollable document. Use an element capture for the header alone, or a clip when the output needs a specific rectangular region.

Can I combine a full-page screenshot with a target element?

The Playwright screenshot tool documentation says full-page capture cannot be combined with a target. Choose one capture boundary for that operation.

Will a fixed header always stay at the top of a full-page screenshot?

There is no universal guarantee in the cited Playwright documentation. Inspect the generated image and use an element or viewport capture if the full-page result does not match the intended state.

Does a larger device scale prevent clipping?

No. Scale controls pixel density. The capture boundary determines whether the header is included.