Puppeteer Screenshot Tutorial: Full Page, Element, and More

3/11/2025

Hey there, Laura and Heidi here from SCRNIFY! 📸

Are you tired of manually taking screenshots for your testing, documentation, or monitoring needs? We've all been there - spending way too much time capturing images, dealing with inconsistent sizes, and struggling to automate the process. It's a real pain!

As developers who've built a screenshot API service, we've wrestled with these exact challenges. That's why we're excited to share this comprehensive guide on using Puppeteer for automated screenshots. Whether you need full-page captures, specific elements, or custom viewports, we've got you covered.

In this tutorial, we'll walk through everything you need to know about taking perfect screenshots with Puppeteer - from basic setup to advanced techniques. By the end, you'll be capturing exactly what you need, when you need it, all automated with clean JavaScript code.

Ready to save hours of manual screenshot work? Let's dive in!

What is Puppeteer and Why Use It for Screenshots?

Puppeteer is a Node.js library that provides a high-level API to control Chrome or Chromium browsers programmatically. It was developed by the Chrome DevTools team and has become the go-to solution for browser automation tasks.

But why choose Puppeteer specifically for taking screenshots? Here's why we love it:

  • Accuracy: Puppeteer renders pages just like Chrome, ensuring your screenshots look exactly like they would in a real browser.
  • Automation: Take hundreds of screenshots across different pages with just a few lines of code.
  • Flexibility: Capture full pages, specific elements, or custom viewports with precise control.
  • Headless Mode: Run without a visible UI for faster performance in production environments.
  • JavaScript Integration: Ideal for JavaScript developers who prefer working with Node.js.

Before Puppeteer, automation options were limited and often unreliable. We remember the days of hacking together solutions with PhantomJS or trying to coordinate Selenium with browser drivers. Trust us, Puppeteer makes things so much simpler! 😅

Installation and Basic Setup

Let's start with the basics. To use Puppeteer, you'll need Node.js installed on your system. Then, you can install Puppeteer using npm:

npm install puppeteer

That's it! This command installs Puppeteer and downloads a compatible version of Chromium automatically.

Now, let's create a basic script that launches a browser and navigates to a page:

const puppeteer = require('puppeteer');

async function basicSetup() {
  // Launch the browser
  const browser = await puppeteer.launch();

  // Open a new page
  const page = await browser.newPage();

  // Navigate to a URL
  await page.goto('https://example.com', {
    waitUntil: 'networkidle2' // Wait until the network is idle
  });

  // We'll add screenshot code here

  // Always close the browser when done
  await browser.close();
}

// Run the function and handle any errors
basicSetup().catch(error => {
  console.error('Something went wrong:', error);
});

This basic setup:

  1. Launches a browser in headless mode (no UI)
  2. Opens a new page (similar to opening a new tab)
  3. Navigates to example.com
  4. Waits for the page to finish loading
  5. Closes the browser properly when done

Now that we have our foundation, let's add screenshot functionality!

Taking a Full-Page Screenshot

One of the most common screenshot needs is capturing an entire webpage, including parts that would normally require scrolling. Here's how to do it:

const puppeteer = require('puppeteer');

async function fullPageScreenshot() {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  try {
    // Navigate to the target website
    await page.goto('https://example.com', {
      waitUntil: 'networkidle2'
    });

    // Take a full-page screenshot
    await page.screenshot({
      path: 'full-page-screenshot.png',
      fullPage: true // This is the magic setting!
    });

    console.log('Full-page screenshot saved!');
  } catch (error) {
    console.error('Error taking screenshot:', error);
  } finally {
    // Always close the browser
    await browser.close();
  }
}

fullPageScreenshot();

The key here is setting fullPage: true in the screenshot options. This tells Puppeteer to scroll through the entire page and stitch together a complete screenshot.

But what about capturing just a specific element on the page? Let's dive into that next.

Taking a Screenshot of a Specific Element

Sometimes you don't need the entire page - you just want to capture a specific button, div, or other element. This is particularly useful for UI testing, creating focused documentation, or monitoring specific parts of an interface.

There are two main approaches to element screenshots with Puppeteer:

Method 1: Using ElementHandle

const puppeteer = require('puppeteer');

async function elementScreenshot() {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  try {
    await page.goto('https://example.com', {
      waitUntil: 'networkidle2'
    });

    // Find the specific element using a CSS selector
    const element = await page.$('h1'); // This selects the first h1 element

    if (!element) {
      console.log('Element not found!');
      return;
    }

    // Take a screenshot of just that element
    await element.screenshot({
      path: 'element-screenshot.png'
    });

    console.log('Element screenshot saved!');
  } catch (error) {
    console.error('Error taking element screenshot:', error);
  } finally {
    await browser.close();
  }
}

elementScreenshot();

In this approach, we use page.$() to select an element using CSS selectors, then call .screenshot() directly on that element.

Method 2: Using Locators (Newer Approach)

The newer versions of Puppeteer support a more powerful locator API, similar to Playwright:

const puppeteer = require('puppeteer');

async function locatorScreenshot() {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  try {
    await page.goto('https://example.com', {
      waitUntil: 'networkidle2'
    });

    // Use a locator to find the element
    const headingLocator = page.locator('h1');

    // Take a screenshot of the located element
    await headingLocator.screenshot({
      path: 'locator-screenshot.png'
    });

    console.log('Locator screenshot saved!');
  } catch (error) {
    console.error('Error taking locator screenshot:', error);
  } finally {
    await browser.close();
  }
}

locatorScreenshot();

Locators offer more flexibility and robustness compared to basic element handles, making them a great choice for more complex scenarios.

Customizing the Viewport

Web pages can look drastically different across screen sizes. With Puppeteer, you can easily customize the viewport size to match specific devices or screen dimensions:

const puppeteer = require('puppeteer');

async function customViewportScreenshot() {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  try {
    // Set a custom viewport size (like an iPhone 12)
    await page.setViewport({
      width: 390,
      height: 844,
      deviceScaleFactor: 2, // For retina/high-dpi screens
      isMobile: true,
      hasTouch: true
    });

    await page.goto('https://example.com', {
      waitUntil: 'networkidle2'
    });

    await page.screenshot({
      path: 'mobile-screenshot.png'
    });

    console.log('Mobile viewport screenshot saved!');
  } catch (error) {
    console.error('Error taking viewport screenshot:', error);
  } finally {
    await browser.close();
  }
}

customViewportScreenshot();

The setViewport method allows you to specify:

  • Width and height in pixels
  • Device scale factor for high-DPI screens
  • Mobile mode simulation
  • Touch event support

This is incredibly useful for testing responsive designs or generating screenshots that match how users actually see your site on different devices.

Waiting for Elements Before Taking Screenshots

One of the most common issues with automated screenshots is timing. What if your page has dynamic content that loads after the initial page render? You need to make sure Puppeteer waits for these elements before taking the screenshot.

Here's how to wait for specific elements to appear:

const puppeteer = require('puppeteer');

async function waitForElementScreenshot() {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  try {
    await page.goto('https://example.com');

    // Wait for a specific element to be visible before taking the screenshot
    await page.waitForSelector('.dynamic-content', {
      visible: true,
      timeout: 5000 // Wait up to 5 seconds
    });

    console.log('Dynamic content is now loaded!');

    // Now take the screenshot
    await page.screenshot({
      path: 'after-wait-screenshot.png',
      fullPage: true
    });

    console.log('Screenshot with dynamic content saved!');
  } catch (error) {
    if (error.name === 'TimeoutError') {
      console.error('Timed out waiting for element to appear:', error);
    } else {
      console.error('Error taking screenshot:', error);
    }
  } finally {
    await browser.close();
  }
}

waitForElementScreenshot();

You can also wait for network activity to complete with:

await page.waitForNavigation({ waitUntil: 'networkidle2' });

Or wait for a specific JavaScript condition to be true:

await page.waitForFunction(() => {
  return document.querySelector('.loading-spinner') === null;
});

These waiting strategies ensure your screenshots capture the page in exactly the state you want.

Handling Pop-ups and Banners

Cookie consent banners, newsletter sign-up forms, and other pop-ups can ruin an otherwise perfect screenshot. Here's how to deal with them:

const puppeteer = require('puppeteer');

async function handlePopupsScreenshot() {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  try {
    await page.goto('https://example.com');

    // Check if a cookie banner exists and click the "Accept" button if it does
    const cookieBannerSelector = '.cookie-banner .accept-button';
    const hasCookieBanner = await page.$(cookieBannerSelector) !== null;

    if (hasCookieBanner) {
      console.log('Cookie banner detected, accepting cookies...');
      await page.click(cookieBannerSelector);

      // Wait a moment for the banner to disappear
      await page.waitForTimeout(500);
    }

    // Now take the screenshot without the banner
    await page.screenshot({
      path: 'no-banner-screenshot.png'
    });

    console.log('Screenshot without banner saved!');
  } catch (error) {
    console.error('Error handling popups:', error);
  } finally {
    await browser.close();
  }
}

handlePopupsScreenshot();

You can adapt this approach for different types of pop-ups by:

  1. Identifying the right selector for the pop-up
  2. Deciding whether to dismiss it, accept it, or ignore it
  3. Making sure you wait for the pop-up to fully disappear before taking your screenshot

If you're dealing with particularly complex or unpredictable pop-ups, you might need to add more sophisticated detection logic, but this pattern will handle most common cases.

Saving Screenshots with Different Options

Puppeteer offers several options for saving screenshots in different formats and qualities:

const puppeteer = require('puppeteer');

async function screenshotOptions() {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  try {
    await page.goto('https://example.com');

    // Save as PNG (default, lossless quality)
    await page.screenshot({
      path: 'screenshot.png',
      // PNG is default, but you can specify it explicitly
      type: 'png'
    });

    // Save as JPEG with quality control
    await page.screenshot({
      path: 'screenshot-quality.jpg',
      type: 'jpeg',
      quality: 80 // 0-100, lower means smaller file size but lower quality
    });

    // Clip to a specific region
    await page.screenshot({
      path: 'screenshot-clipped.png',
      clip: {
        x: 50,
        y: 100,
        width: 500,
        height: 300
      }
    });

    // Omit the path to get a buffer instead of saving to disk
    const buffer = await page.screenshot();
    console.log(`Screenshot captured as buffer, size: ${buffer.length} bytes`);

    // You could then process this buffer however you want
    // For example, upload it to cloud storage, process it with an image library, etc.

    console.log('All screenshots saved with different options!');
  } catch (error) {
    console.error('Error saving screenshots:', error);
  } finally {
    await browser.close();
  }
}

screenshotOptions();

The flexibility to get screenshots as buffers rather than saving to disk is particularly useful if you're building a service or need to process the images further before storage.

Putting It All Together: A Complete Example

Let's bring everything together in a more complete example that demonstrates a real-world screenshot workflow:

const puppeteer = require('puppeteer');
const fs = require('fs').promises;

async function comprehensiveScreenshot() {
  // Create a screenshots directory if it doesn't exist
  try {
    await fs.mkdir('./screenshots', { recursive: true });
  } catch (error) {
    // Directory already exists or cannot be created
    console.error('Error creating screenshots directory:', error);
  }

  // Launch with some useful options
  const browser = await puppeteer.launch({
    headless: 'new', // Use the new headless mode
    args: ['--no-sandbox', '--disable-setuid-sandbox']
  });

  const page = await browser.newPage();

  try {
    // Set a realistic viewport
    await page.setViewport({
      width: 1280,
      height: 800,
      deviceScaleFactor: 1
    });

    // Optional: Set a user agent to avoid being blocked
    await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36');

    // Navigate to the page
    await page.goto('https://news.ycombinator.com', {
      waitUntil: 'networkidle2',
      timeout: 30000
    });

    // Take a full-page screenshot
    await page.screenshot({
      path: './screenshots/hackernews-full.png',
      fullPage: true
    });

    console.log('Full-page screenshot saved!');

    // Take a screenshot of the header
    const headerElement = await page.$('.hnname');
    if (headerElement) {
      await headerElement.screenshot({
        path: './screenshots/hackernews-header.png'
      });
      console.log('Header screenshot saved!');
    } else {
      console.log('Header element not found');
    }

    // Take screenshots of all story titles
    const storyTitles = await page.$$('.titleline > a');
    console.log(`Found ${storyTitles.length} story titles`);

    for (let i = 0; i < Math.min(storyTitles.length, 5); i++) {
      await storyTitles[i].screenshot({
        path: `./screenshots/story-title-${i + 1}.png`
      });
    }

    console.log('Story title screenshots saved!');

    // Get the page as a PDF too (bonus feature!)
    await page.pdf({
      path: './screenshots/hackernews.pdf',
      format: 'A4'
    });

    console.log('PDF version saved!');

  } catch (error) {
    console.error('Error in screenshot workflow:', error);
  } finally {
    await browser.close();
    console.log('Browser closed, all done!');
  }
}

comprehensiveScreenshot().catch(error => {
  console.error('Unhandled error:', error);
});

This comprehensive example:

  1. Creates a dedicated directory for screenshots
  2. Sets up Puppeteer with practical options
  3. Configures a realistic viewport and user agent
  4. Takes multiple types of screenshots (full page, specific elements, multiple elements)
  5. Adds PDF generation as a bonus
  6. Includes proper error handling and cleanup

It demonstrates how you can build a robust screenshot workflow for real-world applications.

Conclusion

Taking screenshots with Puppeteer opens up a world of possibilities for automation, testing, and monitoring. We've covered everything from basic setup to advanced techniques:

  • Installing Puppeteer and basic browser control
  • Capturing full-page screenshots
  • Taking screenshots of specific elements
  • Customizing the viewport for different device sizes
  • Waiting for dynamic content to load
  • Dealing with pop-ups and banners
  • Saving screenshots with different formats and options
  • Building complete screenshot workflows

The best part? All of this can be fully automated and integrated into your existing workflows, whether it's for regression testing, documentation generation, or monitoring critical UI elements.

We've learned these techniques the hard way while building SCRNIFY, and we're excited to see what you'll build with them. While Puppeteer is fantastic for many use cases, remember that setting up and maintaining browser automation at scale can be challenging. If you're looking for a hassle-free solution, get free access to the SCRNIFY API during our open beta and start generating screenshots today! scrnify.com

What screenshot automation are you working on? Have you found other Puppeteer techniques that work well? We'd love to hear from you!

Cheers, Laura & Heidi 🇦🇹

P.S. Don't forget to check out the official Puppeteer documentation for even more screenshot options and browser automation capabilities! And if you found this useful, follow us on Twitter @scrnify for more guides and tips.

Ready to Get Started?

Sign up now and start capturing stunning screenshots in minutes.

Sign Up Now