ScreenshotNeo

BlogHow-to

How to Screenshot a Webpage with Sticky Headers and Fixed Elements

Capture a webpage without sticky headers or fixed controls obscuring or duplicating content. Choose viewport, full-page, or element capture and automate it with Playwright.

By the ScreenshotNeo team4 October 20268 min read

To screenshot a webpage with sticky headers or fixed elements, first choose what the image needs to show: the current viewport, the whole document, or one content element. For a repeatable full-page capture, Playwright can capture the scrollable page; if a fixed bar obscures or repeats in the result, inject a capture-only CSS rule to hide or reposition it, then inspect the image. There is no universal selector or override that works on every site.

For a quick manual capture, Firefox Developer Tools provides full-page screenshots and an Inspector command for a selected node. For automation, Playwright supports both full-page and locator screenshots. The key distinction is that a fixed element is usually anchored to the viewport, while a sticky element sticks within the limits of its scrolling ancestor and containing block.

1. Choose the capture that matches your goal

Goal Capture mode What happens to sticky and fixed elements
Record exactly what a person sees now Viewport screenshot Controls appear in their current position, including overlays.
Show all document content Full-page screenshot The browser captures the scrollable document as a tall image. Inspect whether fixed or sticky items obscure, repeat, or shift in the result.
Show just the article or content region Element screenshot Capture the selected element without including unrelated page controls, if the element bounds are appropriate.
Capture content inside its own scroll area Capture that element or automate scrolling its container A full-page document capture may not expand a nested scrolling region to reveal all its contents.

Full-page capture is not the same as manually viewing every part of a page in one viewport. It creates a tall-page artifact, and browser behavior can vary with page structure. Decide whether the header is part of the evidence you need before hiding or repositioning it.

2. Why sticky and fixed elements behave unexpectedly

position: fixed normally positions an element relative to the viewport in visual media. It stays in place as the document scrolls, which is useful for navigation but can cover content in a screenshot. A transformed, perspective, or filtered ancestor can establish a containing block for a fixed descendant, changing the usual viewport relationship.

position: sticky behaves like relatively positioned content until it reaches an inset threshold such as top: 0. It then sticks relative to its nearest scrolling ancestor, subject to its containing block. Without a non-auto inset on the axis where it should stick, sticky positioning has no threshold to use.

These rules explain why there is no single screenshot fix. The page might have a fixed header, a sticky header, a floating chat widget, a transformed ancestor, or a nested scrolling container. Inspect the actual page and verify the output.

3. Capture manually in Firefox

  1. Open the page in Firefox and open Developer Tools.
  2. For a full-page image, use the Developer Tools full-page screenshot control.
  3. For a specific element, select it in the Inspector and use Screenshot Node.
  4. Alternatively, open the Web Console and run :screenshot --fullpage. To capture a matching element, use the helper’s --selector option, for example :screenshot --selector "main article".
  5. Open the saved image and check whether the header or fixed controls cover content or appear in an unexpected position.

Firefox’s console helper also supports selector-based capture. If a selector matches the wrong element or multiple elements, adjust it and verify the resulting image.

4. Automate full-page and element screenshots with Playwright

Install Playwright in a Node.js project:

npm install -D playwright
npx playwright install chromium

Save this as screenshot.mjs, then run node screenshot.mjs. It captures a full-page image and an element image. Set HIDE_OVERLAYS=1 to apply an illustrative capture-only rule; replace the example selectors with ones from the target site.

import { chromium } from 'playwright';

const url = process.argv[2] ?? 'https://example.com';
const hideOverlays = process.env.HIDE_OVERLAYS === '1';
const browser = await chromium.launch({ headless: true });

try {
  const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
  await page.goto(url, { waitUntil: 'networkidle', timeout: 60_000 });

  if (hideOverlays) {
    await page.addStyleTag({ content: `
      .site-header, .floating-controls {
        position: static !important;
      }
    ` });
  }

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

  const main = page.locator('main');
  if (await main.count() === 1) {
    await main.screenshot({ path: 'main.png' });
  } else {
    console.warn(`Expected one main element; found ${await main.count()}. Skipping element capture.`);
  }
} finally {
  await browser.close();
}

Run it with an example URL:

node screenshot.mjs https://example.com
HIDE_OVERLAYS=1 node screenshot.mjs https://example.com

The position: static override is only an example. It may disrupt layout, fail to affect a widget injected later, or do nothing if the site uses different selectors or script-driven positioning. If the header should be absent, hiding it with a targeted selector may be more suitable than changing its positioning. If it should appear once in the image, repositioning it can help, but check for layout changes. Playwright also supports page.locator(selector).screenshot() for a chosen element; the element must exist and be visible.

Useful Playwright capture choices

  • fullPage: true: captures the full scrollable page as a tall screenshot.
  • page.screenshot() without fullPage: captures the current viewport.
  • page.locator('main').screenshot(): captures the selected element’s bounds.
  • page.addStyleTag({ content }): injects temporary CSS into the page before capture.

For pages with lazy-loaded images, wait for the relevant content to load before taking the screenshot. For content in a nested scroll container, scroll that container and capture its contents or take multiple captures; fullPage describes the document, not every independent scroller.

5. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request can return a PNG, JPEG, WebP, or PDF. Its capture flow accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. It reports page verdict and billing information in response headers, and bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing.

For a full-page capture, use the API’s documented full-page option. Check the ScreenshotNeo API documentation for the current parameter names and options. For this page, a one-call request looks like:

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://example.com \
  -o shot.webp
  • Cookie banners, popups, and chat widgets are removed before the shot.
  • Bot checks, blank pages, and failed loads are never billed.
  • An MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs.
  • 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000 screenshots.

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

6. Troubleshooting

Symptom Likely cause What to try
Header covers text in the full-page image The header remains fixed or sticky during capture. Decide whether it belongs in the artifact. Add a targeted capture-only CSS rule to hide or reposition it, then inspect the image.
Header appears more than once or in an odd location The browser’s full-page capture interacts with positioning in a way that differs from a normal viewport. Try a viewport capture, a locator screenshot, or a targeted CSS override. Verify the result in the browser and screenshot.
CSS override has no effect The selector is wrong, the widget is in an iframe, or page JavaScript changes styles after injection. Inspect the element and selector, inject styles after the element exists, or use a supported automation strategy for that frame.
Header disappears but leaves a large gap Hiding or repositioning the element changed the layout or left space reserved by another wrapper. Target the correct wrapper and inspect the page after applying the rule. Avoid broad selectors that remove structural elements.
Only part of a panel or list is visible The content is inside a nested scrolling container. Scroll and capture that container separately, or capture successive positions if the whole container’s content is needed.
Lazy images are blank The browser has not caused the images to load before capture. Scroll through the relevant page or wait for the image elements to load before taking the screenshot.
Playwright times out at navigation The page may keep network connections open or never reach the selected load condition. Try domcontentloaded and then wait for the specific content selector your capture depends on.
Locator screenshot fails The selector matches no element, multiple elements, or an element that is not visible. Check the locator count, make the target visible, and use a selector that uniquely identifies the intended region.

7. Performance, reliability, and cost

A full-page image can be much taller and larger than a viewport screenshot. Use an element capture when only a content region is needed. Choose a viewport size and device scale that match the intended output, and avoid loading an entire page when the evidence can be captured from one region.

For automation, use a stable readiness condition tied to the content you need. networkidle is convenient, but pages with persistent network activity may not reach it. Waiting for a meaningful selector can be more reliable. When capturing many pages, reuse a browser process where appropriate and close pages and browsers after use.

Local Playwright captures use your own browser runtime and infrastructure; the main costs are compute, storage, and time spent handling browser setup and page-specific exceptions. ScreenshotNeo plans include every feature: Free offers 1,000 shots per month with no card; Starter is $5 for 3,000, Growth $15 for 15,000, Pro $39 for 60,000, Scale $99 for 250,000, and Business $249 for 1,000,000. Yearly billing gives two months free. Only clean shots are billed, and cache hits cost nothing. See ScreenshotNeo for the product and the API documentation for capture options.

8. FAQ

Should I remove a sticky header from every screenshot?

No. Keep it when the screenshot needs to show the interface as a visitor sees it. Remove or reposition it only when it obstructs the content the image is meant to document.

Can a fixed element be fixed relative to something other than the viewport?

Yes. Certain ancestors, including transformed, perspective, or filtered elements, can establish its containing block. Inspect the element’s ancestors if its position does not match the usual viewport behavior.

Does full-page capture include every scrollable panel?

No. A nested scrolling region is distinct from the document’s scrollable page. Capture that region separately or automate its scrolling when you need its hidden contents.

How can I tell whether the screenshot is correct?

Compare it with the intended artifact: check the header position, content near the top and bottom, overlays, and any nested scroll areas. The browser and page structure can affect full-page results, so inspect the saved image.

Sources