DEV Community

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

Generate your social share images automatically in expressjs.

Alex Kluew
Software Engineer, EIT from Canada
Updated on ・3 min read

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).

`"og:image"` and `"twitter:image"` 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}}">
  {{else}}
  <meta name="twitter:card" content="summary">
  {{/if}}

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 https://12313200.ngrok.io/residences/elim-village-british-columbia-reviews, then the image url will be https://12313200.ngrok.io/residences/elim-village-british-columbia-reviews/image. This string is just passed on as data to the template.

For example, the following code


...

res.render(`template`,{
page_image : `https://12313200.ngrok.io/residences/elim-village-british-columbia-reviews/image`
});

...

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="https://12313200.ngrok.io/residences/elim-village-british-columbia-reviews/image">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://12313200.ngrok.io/residences/elim-village-british-columbia-reviews/image">

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

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 :
// https://12313200.ngrok.io/residences/:slug/image
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(`https://12313200.ngrok.io/residences/${slug}`);
  const screenshot = await page.screenshot({
    type: 'png',
    encoding: 'binary'
  });
  await browser.close();
  res.send(screenshot);
});

...

module.exports = router;

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 =)

Thanks,
Alex

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

Discussion (1)

Collapse
getaclue profile image
Alex Kluew Author

by the way if you want to deploy this on heroku, you need to do the following fix : stackoverflow.com/a/55090914

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']
});

vs.

const browser = await puppeteer.launch();

Cheers,
Alex