DEV Community

Cover image for Dynamic Open Graph images with Next.js
Robrecht Meersman for In The Pocket

Posted on • Updated on

Dynamic Open Graph images with Next.js

Update 2022-10: Vercel release a new OG Image Generation library, which does the same thing except 5x faster and at the edge!

This post originally appeared on our ITP Dev blog


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

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, 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:

  <meta property="og:image" content="https://url-to-your-image.png" />
Enter fullscreen mode Exit fullscreen mode

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 that they occasionally use on blog posts or announcements.

GitHub issue example

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 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.


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.

yarn add next-api-og-image
Enter fullscreen mode Exit fullscreen mode

Then configure an API route

// 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,
Enter fullscreen mode Exit fullscreen mode

And call the route from the web page:

// pages/technology/[slug].ts
  <title>Next.js | Tech Radar</title>
Enter fullscreen mode Exit fullscreen mode

and done!

Behind the scenes

As you see in the code above, we are using the 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

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). 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!

  template: {
    html: ({ name, stage }) =>  `<h1>${name} - ${stage}</h1>`,
Enter fullscreen mode Exit fullscreen mode

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 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 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.


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

Top comments (1)

kevinrobert3 profile image

Do you have a live url of the page I can test. Implemented this and Facebook debugger does not detect the image. Nice tutorial