Max SchmittMS
19th July 2019

How I automatically generate social media preview images for my blog posts

When I recently rebuilt my website and blog from scratch, I also created a little web service that lets me dynamically generate social media preview images (or og images, open graph images) that contain the title of the respective post or page in a big font:

Thanks to Puppeteer, this service was super-easy to build. Here is what I did:

I first created a little website that displays whatever text it gets passed as the text query parameter in a big font. The text is put inside a div that's 1200x630 in size:

The code for that site looks something like this:

// GET https://og.maximilianschmitt.me/og-image?text={url-encoded-text}
app.get('/og-image', (req, res, next) => {
const { text } = req.query
res.render('og-image.html', { text })
})

Next, I created another site that takes a screenshot of the HTML page above and returns it as a png-image. This dynamic image page is what I include in my meta tags to point to the social media preview images:

<meta
name="twitter:title"
content="Writing a static site generator with MDX &amp; Webpack - Maximilian Schmitt"
/>
<meta
property="og:image"
content="https://og.maximilianschmitt.me/Writing%20a%20static%20site%20generator%20with%20MDX%20%26%20Webpack.png"
/>
<meta
name="twitter:image"
content="https://og.maximilianschmitt.me/Writing%20a%20static%20site%20generator%20with%20MDX%20%26%20Webpack.png"
/>
<meta name="twitter:card" content="summary_large_image" />

The code to take the screenshot is very simple:

// GET https://og.maximilianschmitt.me/{url-encoded-text}.png
app.get('/:text.png', async (req, res, next) => {
try {
const { text } = req.params
const image = await getOGImageForText(text)
res.send(image)
} catch (err) {
next(err)
}
})
async getOGImageForText(text) {
const urlencodedText = encodeURIComponent(text)
const browser = await puppeteer.launch();
const page = await browser.newPage()
await page.goto(
`http://localhost:${process.env.PORT}/og-image?text=${urlencodedText}`
)
const ogImageElement = await page.$('#og-image')
const image = await ogImageElement.screenshot()
await page.close()
return image
}

Check out the full source code on GitHub.

The og image service is hosted on my Dokku instance.

If you plan on doing something similar, make sure to check the Puppeteer docs on how to run Puppeteer inside Docker.

If you're interested in getting started with Dokku, check out my in-depth tutorial on how to deploy apps and websites with Dokku.