Short answer: Puppeteer’s fullPage: true captures the document’s full extent; it does not convert position: sticky or position: fixed elements into ordinary document-flow content. During capture, the navbar still follows its CSS scrolling ancestor and containing block, so a single tall image can show it at a position that looks like the middle of the page. Inspect the navbar’s computed position and ancestor overflow first, then choose between a normal viewport capture, a capture-only CSS override, or a version-specific screenshot option experiment.
What fullPage actually does
Puppeteer’s ScreenshotOptions reference defines fullPage as taking a screenshot of the full page. That describes the capture extent, not the layout behavior of every element in the image. CSS is still applied while Chromium lays out the page.
A sticky element behaves like a relatively positioned element until its inset threshold (often top: 0) is reached. It then sticks within the relevant containing block and scrolling ancestor. A fixed element is attached to the viewport. When Puppeteer produces one tall image from a page whose layout was designed for scrolling, the navbar can therefore appear at an unexpected vertical location, or appear more than once in stitched output on affected browser versions.
This is not automatically a Puppeteer defect. The result depends on the navbar’s CSS, ancestor overflow, the page’s scroll container, and the Puppeteer/Chromium versions in use.
Recommended Free Tools
Diagnose the navbar before changing the screenshot
1. Read the computed position and inset
Use page.evaluate to inspect the actual element after all styles have loaded. Check both position and inset values such as top, bottom, and z-index.
const info = await page.evaluate(() => {
const el = document.querySelector('nav');
if (!el) return { error: 'nav not found' };
const style = getComputedStyle(el);
const rect = el.getBoundingClientRect();
return {
position: style.position,
top: style.top,
bottom: style.bottom,
zIndex: style.zIndex,
rectTop: rect.top,
rectHeight: rect.height,
className: el.className
};
});
console.log(info);
Run this after navigation and any application hydration. A value of sticky or fixed confirms that the navbar is intentionally outside normal-flow behavior once its threshold is reached.
2. Walk up the ancestor chain
Sticky positioning is governed by the nearest scrolling ancestor, not necessarily by the element that visibly moves when you drag the page. An ancestor with overflow: hidden, scroll, auto, or overlay can establish that scrolling mechanism. Inspect every ancestor and identify the element whose scrollHeight exceeds its clientHeight.
const ancestors = await page.evaluate(() => {
const el = document.querySelector('nav');
const rows = [];
for (let node = el; node; node = node.parentElement) {
const s = getComputedStyle(node);
rows.push({
tag: node.tagName,
id: node.id,
className: node.className,
overflow: s.overflow,
overflowX: s.overflowX,
overflowY: s.overflowY,
scrollHeight: node.scrollHeight,
clientHeight: node.clientHeight
});
}
return rows;
});
console.table(ancestors);
Pay particular attention to an application shell such as .app, main, or a modal layout that owns scrolling while the document itself remains at a different scroll position.
#1 Best Overall
3. Check the page at a normal viewport
Before testing full-page capture, take an ordinary viewport screenshot. If the navbar looks correct there, the discrepancy is caused by the interaction between full-document capture and the page’s scrolling/layout model rather than by the navbar’s basic styling.
await page.screenshot({ path: 'viewport.png' });
Choose the output you actually need
When you need the current browser view
If the requirement is “what a user currently sees,” do not use a full-page image. Set the intended viewport and capture it normally:
await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.screenshot({ path: 'viewport.png' });
This preserves sticky behavior exactly as it appears to a visitor.
When you need one static long image
For documentation, visual regression fixtures, or a page preview, temporarily neutralize the navbar’s sticky/fixed behavior only while capturing. Setting position: static returns it to normal flow; clearing the relevant inset prevents a leftover offset. The override can change layout, so inspect the output and remove the style after the screenshot.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallCrashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteawait page.addStyleTag({ content: `
nav,
.site-nav,
[data-sticky-nav] {
position: static !important;
top: auto !important;
right: auto !important;
bottom: auto !important;
left: auto !important;
}
` });
await page.screenshot({ path: 'full-static.png', fullPage: true });
Replace the selectors with the actual navbar selector. If the navbar is fixed and removed from flow, making it static may move content upward or downward; add a capture-only spacer if the design requires the original reserved height.
Rank #2
- HTML CSS Design and Build Web Sites
- Comes with secure packaging
- It can be a gift option
When an overflow container, not the document, scrolls
If the page uses a fixed-height shell with overflow: auto, fullPage may not represent that shell’s complete scrollable content. Capture the scrolling element itself or temporarily remove the container’s height/overflow for a dedicated static render. Do this only for the capture state, because changing the container can alter responsive breakpoints and lazy-loaded content.
A complete Puppeteer script
The following script records the diagnosis, waits for the page to settle, applies an optional capture-only override, and writes both a viewport image and a static full-page image. Install Puppeteer with npm install puppeteer, then run it with Node.js.
const puppeteer = require('puppeteer');
(async () => {
const url = process.argv[2] || 'https://example.com';
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 1 });
await page.goto(url, { waitUntil: 'networkidle2', timeout: 90000 });
await page.screenshot({ path: 'viewport.png' });
const diagnosis = await page.evaluate(() => {
const nav = document.querySelector('nav, .site-nav, [data-sticky-nav]');
if (!nav) return { error: 'No navbar selector matched' };
const s = getComputedStyle(nav);
return { position: s.position, top: s.top, overflow: s.overflow };
});
console.log('Navbar:', diagnosis);
await page.addStyleTag({ content: `
nav, .site-nav, [data-sticky-nav] {
position: static !important;
top: auto !important;
right: auto !important;
bottom: auto !important;
left: auto !important;
}
` });
await page.screenshot({ path: 'full-static.png', fullPage: true });
await browser.close();
})();
If you need the page’s original sticky behavior in the long image, remove the addStyleTag block and test the result against your Puppeteer and Chromium versions. Do not assume that a visually surprising placement means the script failed.
Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →What captureBeyondViewport can and cannot tell you
captureBeyondViewport is a separate screenshot option. In the current Puppeteer reference, its default is false when there is no clip and true otherwise. It controls capture scope; the reference does not describe it as a universal sticky-position fix.
A 2021 issue participant reported that setting it to false solved a particular case. Treat that as a targeted experiment, not a rule for every current Puppeteer/Chromium combination:
Rank #3
await page.screenshot({
path: 'experiment.png',
fullPage: true,
captureBeyondViewport: false
});
Compare this output with the default, record the exact Puppeteer and Chromium versions, and keep the setting only if it produces the required image without clipping content.
Why viewport resizing often makes things worse
A common workaround is to measure document.body.scrollHeight, resize the viewport to that height, and capture. This can change layouts that use vh, media queries, or viewport-bound fixed elements. A maintainer discussion about historical full-page captures notes that resizing can place fixed or sticky elements incorrectly. Prefer a normal viewport capture or a capture-only CSS override unless you have verified the resized layout at your target pages.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsTroubleshooting common failures
The navbar appears in the middle only with fullPage: true
Confirm that its computed position is sticky or fixed. Capture a normal viewport for a faithful browser view, or apply the static override for a long document image.
The override does nothing
Your selector may not match the rendered element, or a shadow DOM/component stylesheet may own the navbar. Log document.querySelector, target the actual host element, and inspect computed styles after the page has hydrated. An ancestor may also be applying a transform or clipping the element.
The navbar disappears after setting position: static
Fixed elements are removed from normal flow, so the original design may have relied on padding or an explicit spacer. Add a temporary block with the navbar’s height, or apply equivalent top padding to the content in the capture stylesheet.
Rank #4
- Brand: Wiley
- Set of 2 Volumes
- A handy two-book set that uniquely combines related technologies Highly visual format and accessible language makes these books highly effective learning tools Perfect for beginning web designers and front-end developers
The page is clipped or only the shell is captured
Find the real scrolling element. If an ancestor has a constrained height and overflow: auto, document-level fullPage does not automatically mean that nested scroll region is fully rendered. Capture or expand that container deliberately.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Images or content are missing
Wait for the relevant selector, lazy-load images before capture, or wait for network idle plus an application-specific readiness signal. A screenshot option cannot repair content that has not been rendered.
Behavior changed after an upgrade
Record Puppeteer and Chromium versions and reduce the case to a small page. Historical discussions include changes associated with Puppeteer 2.0.0 and upstream Chromium, so older reports should not be treated as current universal bugs.
Best Value
Performance and reliability considerations
- Use a realistic viewport: it keeps responsive breakpoints and
vh-based calculations meaningful. - Wait for layout stability: capture after fonts, images, and client-side rendering have completed; otherwise the navbar’s threshold can move during capture.
- Limit page-side mutations: inject the static stylesheet only for the capture and use a fresh page for the unmodified version.
- Keep diagnostics in CI: log the URL, selector, computed position, scroll container, Puppeteer version, and Chromium revision when a screenshot differs.
- Test representative layouts: desktop, mobile, nested scrolling shells, and pages with consent dialogs can follow different positioning rules.
Or skip the browser setup
If you only need a clean website image or PDF, ScreenshotNeo provides a single HTTP request instead of maintaining a Puppeteer browser. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing result. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
For API parameters and all options, see the ScreenshotNeo documentation. A basic call is:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The same request in Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
And in 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}`);
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
require('fs').writeFileSync('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo supports full-page captures, CSS-selector element captures, custom CSS and JavaScript, waits for selectors/delays/network idle, device presets or custom viewports, dark mode, retina scale, request blocking, cookies and headers, caching, signed links, PDFs, asynchronous jobs, bulk capture of up to 100 URLs per call, and usage reporting. Every feature is included on every plan. The Free plan includes 1,000 shots each month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.
FAQ
Is this always a Puppeteer bug?
No. CSS sticky/fixed rules and ancestor scrolling determine the layout; historical browser issues exist, but current behavior must be checked with your versions and page structure.
Should I always disable sticky positioning?
No. Disable it only when the deliverable is a static long image. Keep it for a viewport screenshot that should match what users see.
Does fullPage include nested scrollable panels?
Not automatically. A nested element with its own overflow and height may need a separate capture strategy.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Frequently Asked Questions
Can I solve this by scrolling the page to the bottom first?
Usually not. Scrolling changes the sticky threshold but does not turn a sticky or fixed navbar into normal-flow content. Choose a viewport capture or apply a capture-only style override.
What should I report when filing a reproducible issue?
Include a minimal URL or HTML case, the navbar and ancestor CSS, Puppeteer version, Chromium revision, viewport dimensions, screenshot options, and whether the document or a nested container scrolls.
Quick Recap
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.




