Dynamic Open Graph images with Next.js
Tags: nextjs, webdev, tutorial, opensource
Published: 2022-03-10 13:10:07

> **Update 2022-10**: Vercel release a new [OG Image Generation](https://vercel.com/blog/introducing-vercel-og-image-generation-fast-dynamic-social-card-images) library, which does the same thing except 5x faster and at the edge! 

---


> This post originally appeared on our [ITP Dev blog](https://inthepocket.dev/posts/2022-03-08-dynamic-open-graph-images/)


## Introduction

Let me start of with a question: which link are you more likely to click on, the left or the right?


![Twitter posts side by side](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ooxhr357x1uwc9btl6hg.png)

Obviously, adding a social media preview image is an great way to improve the professional look of your website and increase your click through rate (though I have no data on this). These social media images are specified using the **[Open Graph Protocol](https://ogp.me/)**, which is a spec introduced by Facebook and now supported by any software that previews URLs for you. The OG image is specified in the document's head:

```html
<head>
  <meta property="og:image" content="https://url-to-your-image.png" />
</head>
```

This can be hooked up to a CMS so editors can choose a picture which suits their landing page, blog post, news article… the most. It is important that **the preview gives the user a clear idea of what to expect** on the webpage, similar to a thumbnail on a YouTube video.

But what if the content of the page is not visual at all, and no photo really suits the contents of the page? And what if we have a lot of these pages, with **dynamically generated content**? Luckily we are not limited to photography, we can use text! The prime example of this is how GitHub generates previews for issue URLs: they generate an image containing enough info such that users know what they will click on. These images are **generated on-demand**, as storing an image for every GitHub pull request or issue would be unfeasable. Another example is Vercel, which even open sourced their [Open Graph Image as a Service](https://github.com/vercel/og-image) that they occasionally use on blog posts or announcements.


![GitHub issue example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tlxv3i4mbrtenr6ywmaj.jpg)

In this article we will find out how we can create these dynamic Open Graph images in Next.js. We will use our In The Pocket [Tech Radar](https://inthepocket.tech) as an example. Our Tech Radar reflects **our current view on software engineering technologies**. It includes the technology choices we’ve made and motivates why we’ve made them. It also shows which technologies are “on our radar”, and why we consider adopting them.  
Each technology has its own webpage, and we have more than 100 technologies on our radar. Manually creating an OG image for each technology would be a boring, repeating task.

## TL;DR

Don't care about the details? Here's a small tutorial on how to add an API route to your Next.js app that generates the images on demand.

First, install [`next-api-og-image`](https://github.com/neg4n/next-api-og-image).

```bash
yarn add next-api-og-image
```

Then configure an API route

```tsx
// pages/api/og-image.ts
import { withOGImage } from 'next-api-og-image';

interface QueryParams {
  stage: string;
  name: string;
}

export default withOGImage<'query', QueryParams>({
  template: {
                                // include HTML template here
    html: ({ name, stage }) =>  `<h1>${name} - ${stage}</h1>`,
  },
  cacheControl: 'public, max-age=604800, immutable',
  dev: {
    inspectHtml: false,
  },
});
```

And call the route from the web page:

```tsx
// pages/technology/[slug].ts
<Head>
  <title>Next.js | Tech Radar</title>
  <meta 
    property="og:image" 
    content="https://inthepocket.tech/api/og-image?name=Next.js&stage=adopt" 
  />
</Head>
```

and done!

## Behind the scenes

As you see in the code above, we are using the [`next-api-og-image`](https://github.com/neg4n/next-api-og-image) library with almost no configuration required to generate the images. Super easy! The interesting part however is **what the library does under the hood**.

The `withOGImage` method exposes a `GET /api/og-image?name=Next.js&stage=adopt` API route with some query parameters we can define ourselves. In the case of our Tech Radar, we use the `name` and `stage` parameters. The API route returns an image containing the name and stage we provided:


![Tech Radar Open Graph image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vqet954s1srffybd8xjy.png)

When requesting the API route, the Next.js serverless function will actually spin up a **web browser on the server** (a headless instance of Chromium, using [`chrome-aws-lambda`](https://github.com/alixaxel/chrome-aws-lambda)). Next, a webpage will be generated with HTML we can define ourselves. This HTML will be used to construct the image. That means that as a developer we can generate images using HTML and CSS, technologies we are already familiar with!

```tsx
  template: {
    html: ({ name, stage }) =>  `<h1>${name} - ${stage}</h1>`,
  },
```

Of course we are not limited to a simple `h1` tag, this can be a full HTML document with styles, images and custom fonts.  

Finally, a **screenshot of the webpage** is taken using [`puppeteer`](https://github.com/puppeteer/puppeteer) and sent to the client-side.

We add some aggressive **caching** (`public, max-age=604800, immutable`) to prevent generating the same image too many times, because the image generation is quite slow and computationally intensive. The way we set up caching is also the reason we pass the query parameters and not just the technology's ID. You might think it is cleaner to fetch the technology's stage and name from the CMS inside the API route, but this makes caching more tricky. By adding all variable elements as query parameters, one URL will always return the same image and we can use the [immutable](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control#immutable) setting on the caching. If the stage or the name of a technology would change on the Tech Radar, we will update the URL, and not update the image behind the URL.

## Conclusion

Adding dynamic Open Graph images in Next.js is very low effort, but makes your site feel 10x more professional!