Dynamically Generate OG Image

Dynamically Generate OG Image blog banner image

Whenever you create your blogging site, or any other website you always want to generate nice preview of the link when you share the link on any social media platform. That image which appears is called Open Graph Image.

One way is to directly use link to static image, which I used to do earlier. But then github popularizes the idea of having a dynamic og images. If you share a link to github issue on Twitter, they generate a image at runtime with all the critical information about the issue on it. And this all Github does on fly when you share a link. This is way better then having static thumbnail.

Let's first see how Github does it and then we will move to how to do it for your own website.

So first thing is you have to specify og:image tag with the link to thumbnail image in header. Some thing like this

1<meta property="og:image" content="link_to_the_image" />

If you checkout og:image tag on any of the github page, and copy the link in it you will get that exact same image.

Idea is simple, you have to take a screenshot of a webpage on fly. To simplify this let's say you want to have following image as your og image

Sample og image

You can easily create a html with this type of layout. Easy right? You have to do exactly this 😲. Just create a html page with this type of layout. And take screenshot of that webpage with Puppeteer.

Lets say you have a route on your site called /generate-og which takes in some argument as url parameters which you can use in your html layout. For example /generate-og?title=<title_of_page>&banner=<banner_url> and whatever you pass as title will replace How I built my website with Nextjs and banner will replace image appearing on right side. Hitting the above endpoint in browser with defined arguments will render a html layout for you with all the information show cased. Then use a software like Puppeteer to take a screenshot of this generated webpage.

One more layer which needs be to added on top of this is caching. Generating brand new image every time is not a good idea. So you may want to store this on some cloud provider like S3 or store on CDN with defined expiry. You can read about this in more detail on Building open graph images

Now lets move on to How I have implemented on this website.

vercel/og

This is a library released by vercel specifically for this purpose. you can use this library to compute and generate social card images using Vercel Edge Functions. This is highly performant as we require a very small amount of code to generate image and thus edge functions can be started almost instantly.

This library completely transforms the way to generate images. Usually, only way to convert your html, css document to image is spin up the headless browser and take screenshot. Vercel itself rolled out another library (engine for HTML) Satori. This can convert your jsx to SVG. Syntax is very simple just provide JSX to satori.

1import satori from 'satori';
2
3const svg = await satori(<div style={{ color: 'black' }}>Satori is Awesome</div>, {
4 width: 600,
5 height: 400,
6 fonts: [
7 {
8 name: 'Roboto',
9 },
10 ],
11});

This will return you a svg string. This provides a huge huge performance benefit.

Now lets see how I have used this library to generate OG image for this site.

Example

First of all you need to create an API route in /pages/api directory. This is endpoint we will hit from [slug].tsx pages. This route can additionally take any parameters useful in our image. For example I have used url of the image to display on right side and title of the blog. On every blog page I can hit /og/?banner=<url>&title=<title>.

But what our API route is supposed to do with all this information 🤔?

Yes you got it right, task of API route is to write a html, css (additionally supports tailwind too 😍) to create a document the way you like to see your social card on twitter, whatsapp etc.

1import { ImageResponse } from '@vercel/og';
2import { NextRequest } from 'next/server';
3
4export const config = {
5 runtime: 'experimental-edge',
6};
7
8export default function handler(req: NextRequest) {
9 try {
10 // get the searchParams from the url
11 const { searchParams } = new URL(req.url);
12
13 return new ImageResponse(
14 <div
15 style={{
16 fontSize: 128,
17 background: 'white',
18 width: '100%',
19 height: '100%',
20 display: 'flex',
21 textAlign: 'center',
22 alignItems: 'center',
23 justifyContent: 'center',
24 }}
25 >
26 {searchParams.get('title')}
27 </div>
28 ),
29 {
30 width: 1200,
31 height: 600,
32 },
33 }
34 catch (e: any) {
35 console.log(`${e.message}`);
36 return new Response(`Failed to generate the image`, {
37 status: 500,
38 });
39 }
40}

Now start the dev server and visit /api/og?title=<any_string> and 💥. You can modify the above document to create any kind of image you can imagine.

Drawbacks

There are a few drawbacks but in most cases shouldn't concern you much.

Currently only edge runtime is supported. And for css, only flexbox and subsets of CSS will work here. Grid and some advanced CSS is not supported. But as I said this should not worry you in most of the cases. You can achieve complex layouts with flexbox too.

Conclusion

Having nice beautiful social card is nice and cherry on top to have for your blogging website or for that matter for any kind of website. This can prompt users to click on the card and visit your site. And with this library it is now a cakewalk to implement in real life.

Subscribe for the newsletter