iToverDose/Software· 23 APRIL 2026 · 16:08

Fix Puppeteer fullPage screenshots with fixed headers: best practices

Puppeteer’s fullPage screenshots often misplace or hide fixed headers, even when the site looks perfect in a real browser. Learn why this happens and how to resolve it with proven code snippets.

DEV Community3 min read0 Comments

Puppeteer’s page.screenshot({ fullPage: true }) command is a powerful way to capture an entire webpage as a single image or PDF. Yet developers frequently encounter a frustrating issue: fixed-position headers—like navigation bars—either vanish, appear in the wrong place, or render inconsistently across multiple pages. When you inspect the site in a standard browser or even in Puppeteer’s viewport mode, everything looks normal. The problem only emerges during full-page captures.

Why does this inconsistency occur?

The core issue: viewport vs. document rendering

When Puppeteer uses fullPage: true, it leverages Chrome’s DevTools Protocol to render content beyond the visible viewport. However, elements with position: fixed are positioned relative to the viewport, not the document. If your script has scrolled to the bottom of the page before taking the screenshot, the fixed header—logically at the top of the viewport—gets captured at the bottom of the rasterized document. The result? A header in the middle of the image, blank space at the top, or the header only appearing on the first page of a multi-page output.

A second, subtler cause compounds the problem: many modern websites apply scroll-behavior: smooth to the root HTML element. When your script calls window.scrollTo(0, 0), the browser initiates a smooth animation to the top. Puppeteer may capture the page before this animation completes, leaving the header in a transitional state—partially visible, misaligned, or hidden.

A reliable fix: reset, scroll, and wait

To ensure consistent full-page screenshots, three steps must be taken immediately before capturing:

  • Disable smooth scrolling so scrollTo(0, 0) is instantaneous.
  • Reset any JavaScript-driven transformations that hide or transform headers during scroll events.
  • Explicitly scroll to the top and allow the browser time to render the updated state.

Here’s a production-ready function that automates this process:

async function prepareForScreenshot(page) {
  await page.evaluate(() => {
    // Disable smooth scrolling across the entire document
    const style = document.createElement('style');
    style.textContent = 'html { scroll-behavior: auto !important; }';
    document.head.appendChild(style);

    // Reset fixed/sticky headers to their default visible state
    const headers = document.querySelectorAll(
      'header, .header, [class*="header" i], nav[class*="header" i]'
    );

    headers.forEach(el => {
      const cs = window.getComputedStyle(el);
      if (cs.position === 'fixed' || cs.position === 'sticky') {
        el.style.setProperty('transform', 'none', 'important');
        el.style.setProperty('opacity', '1', 'important');
        el.style.setProperty('visibility', 'visible', 'important');
        if (cs.display === 'none') {
          el.style.setProperty('display', 'flex', 'important');
        }
        
        // Remove common classes that hide headers on scroll
        ['hidden', 'is-hidden', 'scroll-up', 'scroll-down', 'header--hidden']
          .forEach(c => el.classList.remove(c));
      }
    });

    // Force immediate scroll to top
    window.scrollTo(0, 0);
  });

  // Allow one render frame for changes to settle
  await new Promise(r => setTimeout(r, 300));
}

Call this function just before your screenshot command:

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

Handling advanced cases: synthetic events and overlays

For most static or moderately dynamic sites, the fix above resolves the issue entirely. However, some websites use JavaScript scroll event listeners that re-evaluate header visibility after your reset code runs. In such cases, dispatching a synthetic scroll event can prompt the site’s own logic to re-hide the header correctly:

window.scrollTo(0, 0);
window.dispatchEvent(new Event('scroll'));

Additionally, sites with transparent fixed headers overlaid on hero sections (a common modern design pattern) typically render correctly once the viewport and document alignment are fixed. The header simply overlays the hero image as intended.

A practical tool built on these fixes

This exact solution powers Site2PDF, a service that converts websites into PDF, PNG, JPG, or ZIP archives. The platform offers a free tier with five archives per month, including advanced options like cookie banner removal, sticky header unfixing, and accordion expansion. The tool is designed to bring browser-like fidelity to automated exports—without the quirks of headless rendering.

As web applications grow more interactive, automated screenshot reliability becomes critical. Whether you're auditing UI consistency, generating documentation, or archiving content, mastering Puppeteer’s fullPage behavior ensures your outputs reflect the true user experience.

AI summary

Stop Puppeteer fullPage screenshots from hiding or misplacing fixed headers. Learn the root cause and apply a proven fix with JavaScript and CSS tweaks for accurate screenshots.

Comments

00
LEAVE A COMMENT
ID #VK21HX

0 / 1200 CHARACTERS

Human check

3 + 4 = ?

Will appear after editor review

Moderation · Spam protection active

No approved comments yet. Be first.