DEV Community

Cover image for Generate your social share images automatically in expressjs.
Alex Kluew
Alex Kluew

Posted on • Updated on

Generate your social share images automatically in expressjs.

I wanted to do exactly as my cover image shows. I wanted to add an image to my social media cards and I wanted this image to be generated on the fly. I used twitter's card preview feature to test my implementation.

In my express.js app I wanted to see if I could generate images of a web page. So, I went with the idea of taking web page screenshot and, then, using this screenshot as my social media card. This is done by setting the two image properties in my meta tags (just as I show you below).

 raw `"og:image"` endraw  and  raw `"twitter:image"` endraw  meta tags

The two SEO image tags that I needed to be dynamic were : "og:image" and "twitter:image". I adjusted the express.js project by going into my handlebarsjs layout template and adding an if statement.

The if statement simply looks for the presence of page_image variable as one of the attributes passed on to the template. If the variable exists, then simply populate its content wherever we need it. Or, in other words :

 {{#if page_image}}
  <meta name="og:image" content="{{page_image}}">
  <meta name="twitter:card" content="summary_large_image">
  <meta name="twitter:image" content="{{page_image}}">
  <meta name="twitter:card" content="summary">
Enter fullscreen mode Exit fullscreen mode

From above, page_image variable holds just a simple string that show the location to my dynamic image generation function. The string is a combination of simply taking a residence slug and adding /image to it.

So, if we are rendering the following page, then the image url will be This string is just passed on as data to the template.

For example, the following code


page_image : ``


Enter fullscreen mode Exit fullscreen mode

would translate to the if statement above evaluating to true, in the handlebarsjs template, and the attached html code of the block was :

<meta name="og:image" content="">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="">
Enter fullscreen mode Exit fullscreen mode

Perfect, now our routes are dynamic just like I wanted. Now, I needed to implement the actual router.get('/image') function. We go to our terminal and type in the following to install puppeteer and add it to our project

npm install puppeteer --save
Enter fullscreen mode Exit fullscreen mode

then we just code the end point that we want above. Mine looked like this :

// ./routes/residences.js
const express = require('express');
const puppeteer = require('puppeteer');

const router = express.Router();


// equivalent to :
router.get('/:slug/image', async (req, res, next) => {
  const { slug } = req.params; 
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto(`${slug}`);
  const screenshot = await page.screenshot({
    type: 'png',
    encoding: 'binary'
  await browser.close();


module.exports = router;
Enter fullscreen mode Exit fullscreen mode

Woot! Added a new get /image route that sends a dynamic screenshot image of the webpage. Just as the cover image shows, the newly created dynamic image adds a bit more value to the existing social cards. Your cards now show to the user exactly what the page looks like before they think of clicking on the social card to view it. If they click on the card and go to the actual page, then they view a familiar UI that was presented to them in the social card.

There you have it. Have you tried something similar? Do you have suggestions or improvements to my code? Please let me know =)


PS: I used ngrok for this experiment so don't try to actually access the url 🀣

Top comments (1)

getaclue profile image
Alex Kluew

by the way if you want to deploy this on heroku, you need to do the following fix :

which involves :

  1. adding a build pack
  2. changed the way puppeteer is instantiated :
const browser = await puppeteer.launch({
      args: ['--no-sandbox', '--disable-setuid-sandbox']


const browser = await puppeteer.launch();