Static Open Graph images

Table Of Contents

  1. A small intro
  2. My initial approach
  3. Using Puppeteer
  4. Vercel's solution
  5. The one issue with Vercel's solution
  6. Current approach

#A small intro

When we share a link to a webpage on Facebook or Twitter or any other social media platform, we get a preview card. Like this one, for example, from Twitter.

Preview Card Example

There's some information about the link it's referring to, such as the title. Each information mentioned on the card, is specified using a set of meta tags. Specifically, these:

<meta
    property="og:description"
    content="How much time would you need to check if a given number is divisible by 2 –15 without a computer? Well, for the first few numbers, you would be so fast. But you will get stuck at 7 or 13 right? Don't worry. After reading this article, you won't. Give it a try."
/>
<meta
    property="og:title"
    content="Divisibility Rules - An Explanation | Sahithyan"
/>
<meta property="og:url" content="sahithyan.dev" />
<meta
    property="og:image"
    content="https://sahithyan.dev/og-images/divisibility-rules.png"
/>

These (the ones with property="og:...") are the Open Graph meta tags. These meta tags define meta data a social graph would find useful, such as, the language used in the website. You can learn more about these tags in the official website of Open Graph Protocol.

Among all the OG tags, I think og:image is an important one. We can define what image should be shown in the preview card (You can see how, in the above code snippet). Usually, links with an OG image defined, drive more clicks compared to the ones that don't. They are like a free advertising space for us. Also, it reduces time took by the viewers to understand what's this link is pointing to.

In this post, I want to talk about how I managed to add OG images for the posts and other pages on this website.

#My initial approach

Initially I created a blank image with my face, and used it as the OG image for all the pages.

The default OG image

Yeah, I know, it's not that nice looking and I shouldn't be using the same one for all the pages. But something is better than nothing. Now the viewers have something to see now.

However, my goal was to include more information than just my face in the OG images. I started looking for another solution.

#Using Puppeteer

As I was searching for alternate approaches and I stumbled upon GitHub's blog post on building Open Graph Images. I thought it would be great to do the same for my blog —or to at least give it a try—. So I followed their approach, and used Puppeteer to generate screenshots of a HTML page at build time. This allowed me to generate visually appealing OG images specific to each post.

Here is an example:

OG image of Divisibility Rules

It's obviously so much better than using a fairly blank image. Now, the viewers will know about the post at the first sight.

There is one trade-off though: long build times.Puppeteer is a tool that uses a headless version of Chrome. Its resource-intensive nature makes it time-consuming to use.
On top of that, I was already using Puppeteer to prerender the pages at build time. This means, I am now running two separate Puppeteer instances(!) on each build. This would have increased the time by at least 30s; believe me, that's too long.

I was ignoring this issue because I felt like there's no simple solutions for this issue. Thankfully, Vercel had one.

#Vercel's solution

About a month ago, Vercel announced OG Image Generation on Edge network. This can be achieved using the @vercel/og library, which allows the generation of OG images from HTML/CSS on API routes, on-the-fly. Instead of using Puppeteer, this library utilizes Satori — a library to convert HTML/CSS to SVG — and Resvg — a library to convert SVG to PNG —. More information can be found in @vercel/og - Vercel's Docs.

In their announcement, they also mentioned that they saw 5x performance improvement with @vercel/og compared to the Puppeteer-based solution. It mostly took less than a second. That's so amazing. However...

#The one issue with Vercel's solution

@vercel/og doesn't support the Node.js runtime; only the Edge Runtime.

Default Node.js runtime is not supported by @vercel/og

They mentioned "Node.js runtime will not work". Does that mean it may will, in the future? I have no idea.

But I wanted to use it in build time to generate the images. I looked for an alternative that builds on top Satori (core library of @vercel/og) and supports Node.js. I couldn't find anything. So, I built one using the core libraries @vercel/og uses.

So, if you are someone looking to run @vercel/og locally, you can use @sahithyan/og. 😀

#Current approach

Currently, I am using @sahithyan/og to generate the OG images.

As my website already takes a lot of time to build, I have set the script up to run on every commit, instead of on every build. It's done using husky and lint-staged. And now, OG images will be created only for the posts that are being committed locally.
Other pages are still using the default one I initially used, though.