Take better screenshots with Puppeteer
Alex Araiza
Posted on October 13, 2021
Images are an important part of every website. They help convey meaning and emotion, and they can make any design more attractive—or less, depending on how you use them. So whether it's photographs, illustrations or renders, getting good images is an important step in web development.
Using screenshots is a common practice, especially for web developers showcasing their work in a portfolio; but the most popular screen capturing tools don't allow for much customization, so the quality of the resulting images may not be good enough. Thankfully, there are other tools; like Puppeteer, which does allow for plenty of customization.
What is Puppeteer?
Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the DevTools Protocol. Puppeteer runs headless by default, but can be configured to run full (non-headless) Chrome or Chromium.
When you install Puppeteer, it downloads a recent version of Chromium (~170MB Mac, ~282MB Linux, ~280MB Win) that is guaranteed to work with the API.
You can automate many tasks with Puppeteer, one of them being taking screenshots.
Getting started
To get started, you need to install Puppeteer:
npm install puppeteer
or
yarn add puppeteer
Then, create a file with the following code:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://www.w3schools.com/howto/tryhow_css_example_website.htm');
await page.screenshot({ path: 'example.png' });
await browser.close();
})();
The code above takes a screenshot of https://www.w3schools.com/howto/tryhow_css_example_website.htm and saves it to example.png.
Resolution
Screenshots are 800x600px by default, but you can change their resolution:
await page.setViewport({
width: 640,
height: 480,
deviceScaleFactor: 1
});
setViewport
resizes the page, so if the page you want to screenshot doesn't handle viewport resizing well, you may want to call this method before calling goto
.
Changing deviceScaleFactor
lets you get an image with a different resolution without changing the size of the viewport. This can be useful when you want a high-resolution image with a page layout that is specific to a certain viewport size.
Waiting for elements to load
It's possible that some elements aren't fully loaded when the screenshot is taken. You can make your script wait for them to load using two different methods:
await page.waitForSelector('img');
await page.waitForXPath('//img');
Both methods will return as soon as the specified selector or XPath exists, so they will only wait for the first element with the specified selector or XPath to load.
Waiting some more
You can also make your script wait for a number of milliseconds:
await page.waitForTimeout(1000);
This can be useful to make your script wait for an animation to finish before taking a screenshot (or to make your script wait for elements to load if you're feeling lazy and don't want to get the selector or XPath of the elements).
It's generally recommended to not wait for a number of seconds, but instead use Page.waitForSelector(), Page.waitForXPath() or Page.waitForFunction() to wait for exactly the conditions you want.
Full page screenshot
You can take screenshots of the full scrollable page by setting fullPage
to true
:
await page.screenshot({ path: 'example.png', fullPage: true });
Specific area of the page
You can take screenshots of a specific area of the page by setting clip
to an object with the x and y coordinates of the top-left corner of the area, and the width and height of the area:
await page.screenshot({
clip: {
x: 50,
y: 100,
width: 150,
height: 100
},
});
Transparency
You can take screenshots without the default white background by setting omitBackground
to true
:
await page.screenshot({ path: 'example.png', omitBackground: true });
Keep in mind that this only applies to the default background, so if the background of any element is not transparent, it will show on the screenshot. Not to worry, you can set the background of any element to be transparent by running JavaScript in the page's context.
Running JavaScript
You can run a JavaScript function in the page's context:
await page.evaluate(() => {
let element = document.getElementById('elementWithBackground');
element.style.background = 'transparent';
document.body.style.background = 'transparent';
});
The code above sets the background of the element with ID 'elementWithBackground' and the body to be transparent, but you can modify the page in any way so you can get the exact screenshot you need.
Mind the size
Part of making the user experience great is making sure that the end user can see the images, and that isn't going to happen if they're using a 3G phone while trying to download a 42MB image. Make sure the images you send to the end user are an appropriate size!
Conclusion
This post should help you get most of the screenshots you need, but you can always take a look at the Puppeteer documentation to learn more.
Here's some code with most of the methods mentioned in this post:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({
width: 640,
height: 480,
deviceScaleFactor: 1
});
await page.goto('https://www.w3schools.com/howto/tryhow_css_example_website.htm');
await page.waitForSelector('img');
await page.waitForTimeout(1000);
await page.evaluate(() => {
let element = document.getElementById('elementWithBackground');
element.style.background = 'transparent';
document.body.style.background = 'transparent';
});
await page.screenshot({ path: 'example.png', fullPage: true, omitBackground: true });
await browser.close();
})();
Feel free to modify this however you want. Have fun with it!
Posted on October 13, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.