Now with Dynamic Open Graph Images!
Inspired by GitHub's pretty sweet post about their Open Graph images framework, I decided to try to build something similar for my site (at a smaller scale, obviously!). I wanted to build this for my blog ever since I saw the folks at Vercel do it.
I used Puppeteer to take a screenshot of a very simple HTML template, hooked it into my website's deployment pipeline, and voilà: dynamic OG images!
There's still some tweaks I wanna make to the template to make it nicer, but it's looking clean!
I wrote generator code that gets executed as part of my next export
process, this is the full script:
import chromium from "chrome-aws-lambda";
import format from "date-fns/format";
import fs from "fs";
import path from "path";
import posts from "./posts";
export async function generate({ outDir }) {
const browser = await chromium.puppeteer.launch({
args: chromium.args,
executablePath: await chromium.executablePath,
headless: true,
});
const page = await browser.newPage();
// Open the HTML template as a data URI
await page.goto(
`data:text/html;charset=UTF-8,${encodeURIComponent(TEMPLATE)}`,
{ waitUntil: "networkidle0" }
);
// Wait until the document is fully rendered
await page.evaluateHandle("document.fonts.ready");
// Set the viewport size to match
await page.setViewport({
width: 1200,
height: 632,
// Netlify build machines do not have a
// retina display, apparently xD
deviceScaleFactor: process.env.NETLIFY ? 1 : 2,
});
const ogImagesDir = path.resolve(outDir, OG_IMAGES_PATH);
// iterate over each post
for (const post of posts) {
const info = {
title: post.title,
date: format(post.date, "dddd, MMMM Do, YYYY"),
};
await page.evaluate((info) => {
const titleElement = document.querySelector("#title");
const dateElement = document.querySelector("#date");
titleElement.innerHTML = info.title;
dateElement.innerHTML = info.date;
}, info);
await page.screenshot({
path: `${ogImagesDir}/${post.id}.png`,
type: "png",
clip: { x: 0, y: 0, width: 1200, height: 632 },
});
}
// Wrap it up
await browser.close();
}
This code heavily inspired by vercel/og-image's code.