Using Puppeteer to Keep SEO Thumbnails Current

Published on March 8th, 2023

Using Puppeteer to Keep SEO Thumbnails Current image

I have a few websites which I keep up to date, and one of the things that caused me a recurring headache was remembering to update my open graph meta tag preview images every time I made a change. Reshooting preview images with my old flow was cumbersome; I'd have to fire up my local dev server, set my localStorage data stores to get the display elements to look correct, scale the browser window to just the right dimensions, then finally attempt to capture the screen down to the exact pixel (which usually took a few attempts).

In my ongoing effort to automate all the things, I spent some time with Puppeteer and relieved myself of this grunt work.

Automate all the things!

Configuration Steps

1. Install Puppeteer

First we'll install Puppeteer into our project, using the --save-dev flag since we'll only need to use this marionette in development.

npm install puppeteer --save-dev

2. The Script

My script happens to live at /src/utils/marketingFunctions.js, but anywhere under /src should be just fine.

Once you have a home for our new script, let's flesh it out with some functionality. If you don't have any fresh JavaScript, store-bought is fine:

const puppeteer = require("puppeteer");

const DEVELOPMENT_URL = "http://localhost:3000/coded/gotahopuphere/";

(async () => {
  // 1. Launch the (headless) browser
  const browser = await puppeteer.launch({
    defaultViewport: {
      width: 1200,
      height: 630,
    args: [
      // any other puppeteer arguments you may need go here

  // 2. Build the display data object
  const tempData = {
    seasonalAchievedLevel: new Array(CURRENT_SEASON_NUMBER).fill(null),
    seasonalTreasurePacks: new Array(CURRENT_SEASON_NUMBER).fill(null),
    seasonalPacks: [],
    seasonalQuestPacks: new Array(CURRENT_SEASON_NUMBER).fill(null),

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

  // 4. Navigate to URL
  await page.goto(DEVELOPMENT_URL);

  // 5. Load the temp localStorage data
  await page.evaluate((tempData) => {
    localStorage.setItem("heirloomData", JSON.stringify(tempData));
    localStorage.setItem("player-rank", '{"tier":"Silver","division":"IV"}');
  }, tempData);
  // 5a. Reload to display updated data model
  await page.goto(DEVELOPMENT_URL);

  // 6. Take screenshot #1
  await page.screenshot({
    path: "./public/gotahopuphere-dot-com.jpg",
    type: "jpeg",
    quality: 90,

  // 7. Load dark localStorage data
  await page.evaluate(() => {
    localStorage.setItem("color-scheme", '"dark"');
  // 7a. Reload to display updated data model
  await page.goto(DEVELOPMENT_URL);

  // 8. Take screenshot #2
  await page.screenshot({
    path: "./public/gotahopuphere-dot-com-dark.jpg",
    type: "jpeg",
    quality: 90,

  // 9. Alert the console

  // 10. Finish up
  await browser.close();

4. Wire it Up

Finally, our last configuration step is to take our shiny new script and hook it into our package.json for ease of use. Head into your project's package.json and find the "scripts" object, and add a command which calls our new marketing function:

"scripts": {
	"screenshot": "node ./src/utils/marketingFunctions.js"

5. Try It!

The time is time, pop open a new terminal tab with your project running and run:

npm run screenshot

After a few moments, you should have freshly updated screenshots that look correct according to the data model you set up, dropped into place in your project ready for your next deploy.

Wrapping Up

This was yet another step in my quest of automating "all the things," and it seems like a great future opportunity to wire in this screenshot step into my GitHub CI/CD pipeline.

Automating away Open Graph metadata screenshots has saved me a ton of time - and frustration. I encourage you on every project to dive deeply (and infrequently!) into automation to push away little tasks like this over to the robots. Happy generating!

Headshot of Mike Zarandona

Huge nerd, hobby collector, happy husband & dad. The personal website of Mike Zarandona.

How to Configure Synology Shares with NFS
Run Your Own VPN Server for Privacy, Security, and Anti-Censorship

© 2023 Mike Zarandona