Puppeteer Screenshot में वेबसाइट का Header कट रहा है: कैसे ठीक करें
A Puppeteer screenshot can cut off a header because of capture bounds, clipping, oversized elements, or fixed positioning. Diagnose the capture mode first.
If a Puppeteer screenshot cuts off a website header, first identify whether you are capturing the viewport, the full page, or just the header element. The cause may be the screenshot bounds, a clip rectangle, an element larger than the viewport, or fixed/sticky positioning during full-page capture. Check those cases before changing the site’s CSS.
The examples below use Puppeteer’s documented screenshot options. Defaults and behavior can depend on whether clip is present, so make the intended capture mode explicit. See the ScreenshotOptions API and the screenshots guide.
1. Identify what is being captured
| Capture call | What it is intended to show | First thing to inspect |
|---|---|---|
page.screenshot() |
The current viewport by default | Viewport size, scroll position, and any clip |
page.screenshot({ fullPage: true }) |
The full document | Fixed/sticky positioning and nested overflow regions |
element.screenshot() |
The selected element | Element bounds and whether it exceeds the viewport |
Write down the Puppeteer version, viewport width and height, device scale factor if set, the screenshot call and options, and which edge is missing. A header cut off at the top suggests a different problem from one clipped by the right or bottom edge of a custom rectangle.
2. Reproduce the capture modes with a minimal script
This runnable JavaScript example captures the viewport, the full page, and a selected header. Replace the URL and selector with the page you are investigating. Install Puppeteer in your project with npm install puppeteer, then run the script with Node.js.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setViewport({ width: 1365, height: 900, deviceScaleFactor: 1 });
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
// Default: capture the visible viewport.
await page.screenshot({ path: 'viewport.png' });
// Capture the full document.
await page.screenshot({ path: 'full-page.png', fullPage: true });
// Capture the selected header element.
const header = await page.$('header');
if (!header) throw new Error('No element matched the header selector');
console.log('Header bounds:', await header.boundingBox());
await header.screenshot({ path: 'header.png' });
} finally {
await browser.close();
}
})().catch(error => {
console.error(error);
process.exitCode = 1;
});
networkidle2 is the wait condition used in Puppeteer’s guide example; it is not a guarantee that every application has finished rendering. For pages that load content after navigation, wait for a meaningful selector or application-specific ready state before taking the screenshot.
3. Fix viewport-only screenshots
A normal page.screenshot() captures the viewport unless you request full-page capture. If the header is partly outside that viewport, check the configured viewport and page scroll position. Keep the viewport at the dimensions you actually need to represent: changing it can trigger responsive breakpoints, alter vh-based layouts, and fire resize handlers.
await page.setViewport({ width: 1365, height: 900, deviceScaleFactor: 1 });
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
await page.evaluate(() => window.scrollTo(0, 0));
await page.screenshot({ path: 'top-of-viewport.png' });
If the page is intentionally scrolled, capture that state deliberately. For a header that should remain visible while scrolling, verify its actual browser behavior at the target viewport before changing screenshot settings.
4. Check full-page capture and fixed or sticky headers
Use fullPage: true only when the desired output is the entire document. A full-page screenshot and a real viewport screenshot answer different questions. If the header is wrong only in the full-page image, inspect position: fixed or sticky, nested scrolling containers, and overflow clipping. Decide whether the output should depict one real viewport or the expanded document.
Puppeteer’s repository discussion records a v2.0.0 change adopting Chromium viewport clipping behavior. The discussion addressed fixed headers and cookie banners appearing in the middle of screenshots under earlier behavior; it is historical implementation context, not a guarantee for every current layout. See Puppeteer issue #5080.
// Compare these two outputs before changing page CSS.
await page.screenshot({ path: 'viewport.png', fullPage: false });
await page.screenshot({ path: 'document.png', fullPage: true });
If nested content scrolls inside an element with overflow: auto or overflow: hidden, full-page document capture may not represent the scrolled contents as you expect. Inspect the scroll container and decide whether it needs to be scrolled or captured as a separate element.
5. Inspect clip rectangles and capture bounds
A screenshot clip is a rectangle with x, y, width, and height. If it intersects only part of the header, the screenshot will too. Temporarily remove the clip to see whether it is responsible, then compare each coordinate with the desired region.
// Diagnose without a clip first.
await page.screenshot({ path: 'no-clip.png' });
// Add a clip only when a specific region is intended.
await page.screenshot({
path: 'header-region.png',
clip: { x: 0, y: 0, width: 1365, height: 180 },
captureBeyondViewport: true
});
In the documented options, captureBeyondViewport defaults to false without a clip and true with a clip. Set it explicitly when using a clip so the intended behavior is clear, and confirm that your installed Puppeteer version supports the option. See the API reference.
6. Capture a header element safely
ElementHandle.screenshot() scrolls a hidden element into view before capturing it. That does not mean every oversized element can be captured without clipping. Select the exact header, wait until it exists, and inspect its bounding box. Puppeteer’s guide covers both page and element screenshots: Screenshots guide.
const header = await page.waitForSelector('header', { visible: true });
const bounds = await header.boundingBox();
if (!bounds) throw new Error('Header has no visible bounding box');
console.log(bounds);
await header.screenshot({ path: 'header.png' });
If the box is larger than the viewport, a controlled viewport increase can help diagnose a viewport constraint. Compare the layout before and after resizing, because responsive rules and resize handlers may change the header. Restore the original viewport if the resized capture no longer represents the target device. A historical report of element clipping in Puppeteer 0.13.0 describes this kind of viewport constraint and its layout side effects: issue #1779.
7. Troubleshooting checklist
| Symptom | Likely cause | What to try |
|---|---|---|
| Only the ordinary screenshot is cut off | The header is outside the viewport, the page is scrolled, or viewport dimensions differ from expectations | Log viewport dimensions, scroll to the intended position, and capture without a clip |
| Only full-page capture is wrong | Fixed/sticky positioning, nested overflow scrolling, or a mismatch between viewport and document capture intent | Compare viewport and full-page outputs; inspect positioning and scroll containers |
| The same exact edge is missing each time | A clip rectangle is too small or offset | Remove clip, then verify x, y, width, and height |
| Element screenshot cuts off a large header | The element extends beyond the available viewport or its box differs from the visible design | Inspect boundingBox(); test a controlled viewport change and compare responsive layout |
| Header is absent or stale | The selector did not match, the element was not visible, or asynchronous rendering was incomplete | Use waitForSelector with the intended selector and wait for the application’s ready state |
| Layout changes after increasing viewport | A media query, vh sizing, or resize listener changed the page |
Capture at the target viewport and use the resize only as a diagnostic |
| Option is rejected or behaves differently | Installed Puppeteer version and documentation may not match | Check the package version and use the matching official API reference |
For a site-specific diagnosis, collect the Puppeteer version, viewport dimensions and device scale factor, exact screenshot call and options, whether the call is page or element capture, the clipped edge, and the header’s positioning and overflow context.
8. Performance, reliability, and cost
- Capture only what you need. Viewport screenshots generally involve less page area than full-document captures; use full-page mode when the complete document is required.
- Use a meaningful readiness condition. Network idle can be useful, but apps with ongoing requests or delayed rendering may need a selector or app-specific ready signal.
- Keep viewport settings stable. Reusing the intended dimensions makes comparisons reproducible and avoids accidental breakpoint changes.
- Record capture parameters. Save the Puppeteer version, viewport, screenshot options, and selector with the output so a changed image can be traced to a changed input.
- Cost depends on your runtime and hosting. The cited Puppeteer sources do not give a universal cost or speed benchmark. Measure your own workload and infrastructure rather than relying on a made-up comparison.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server for developers. Make one GET request to return an image or PDF, with no local browser setup. See the ScreenshotNeo API documentation.
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 require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
- Cookie banners are accepted like a visitor, and 60+ known consent platforms, newsletter popups, and chat widgets are removed before the shot. Each step can be turned off.
- Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers say the page verdict and whether it was 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. Every feature is on every plan.
Sign up for ScreenshotNeo’s free plan and get 1,000 screenshots a month with no card.
FAQ
Should I set fullPage: true for every screenshot?
No. Set it when the desired image is the full document. Leave it off for a browser-viewport capture.
Does ElementHandle.screenshot() automatically fix clipping?
It scrolls a hidden element into view, but an element larger than the viewport can still present a bounds problem.
Should I change the website CSS to fix a screenshot?
Only after checking capture mode, clip bounds, element dimensions, and the target viewport. The supplied title does not identify a particular site defect.
Is a larger viewport always the right fix?
No. It can alter responsive layout and resize-dependent behavior. Treat it as a diagnostic or use it only if the larger viewport is the intended capture environment.


