Generating Open Graph previews for static sites

Ok, I'm really excited about this one! In this post we'll talk about how to generate preview images for sharing to social media.

For example, here's the preview image for this post you're reading now:

An example of a generated Open Graph image, created for this post

What's interesting about this image is that it was generated, by plugging some data into an HTML template and then programmatically screenshot'ing the result.

What previews, and why?

If you're not familiar with Open Graph, it's a protocol that's widely standardized as the way to provide metadata about a URL that is shared: the author, a description, and an image or video preview... when you see articles shared on Twitter, Facebook, Reddit, etc. they're all using Open Graph (or a variation) to scrape the content to put the previews together.

Opting in to Open Graph is pretty simple: you add some <meta /> elements to the source HTML for the page, and those elements contain the relevant data... there are quite a few possible values, but they are well-documented and examples are easy to find. For this blog, I add these tags to each post page:

While including the tags isn't too bad, I don't want to manage a distinct graphical asset for each blog post... I want to participate in better sharing but I'm not a graphic designer, nor a photo-journalist. I'd much rather generate a reasonable preview of each post from code if possible, which is where this journey began.

HTML as a drawing tool

It's perhaps a bit trite coming from a UI dev, but I'm pretty comfortable getting things rendered using web tech. HTML, CSS and friends can be used to create graphics for a variety of use cases, and sure I may be a hammer, but this definitely looks like a nail.

The first step is to create some HTML / CSS to use as a preview. The actual content of the template HTML is not the point here, what's important is that you'll need some HTML that visually represents a preview image.

Taking screenshots with Playwright

Once you have an HTML template, we can move on to generating images from it... I'm using Playwright, but as mentioned before you can also use Puppeteer in a nearly-identical way.

The docs for taking screenshots are pretty clear, so let's start there:

...ok, so far so good. But going back to our goal of making this build-time only, we can already see that we'll have to change this example to not reference a host.

Even with Playwright getting us so close, how do we take a picture of a webpage without serving a webpage?

Browsers will render pretty much anything

Instead of visiting a host with Playwright, we're going to read the HTML from the file-system and then load it as a data URL.

I have this as an NPM script at the moment, because I don't need to run it often. Depending on how your how your project is structured, your mileage may vary but something similar should work.

Some other considerations

There are a few other things I learned while working on this:

  • You probably want a bigger preview image than you think. I started out with a 600x300 image because it was so easy to design the HTML template for, but the image came out grainy. The example here has been scaled up to double that
  • You'll also get better images if you make sure to set the deviceScaleFactor to 2 when creating the new Playwright context. This helps ensure the pixel density is high enough in Playwright to produce reasonable-fidelity screenshots
  • Remember that the point of Open Graph integration is to convey additional meaning when sharing URLs: generating preview images is cool, but make sure they're meaningful. You're trying to entice a passing-by reader, presumably in a way that plain-text won't.

I hope this helps you share your creations mores easily, and as always thanks for reading!