Creating Dynamic OG Images for Your Next.js Application with Serverless Functions
Aymen kani|January 9th 2023

If you have a blog or website, you may want to create open graph (OG) images to use when sharing your content on social media platforms. OG images are displayed when someone shares your content on social media, and they help to give context to the shared content and make it more visually appealing.

In this tutorial, we'll look at how to create dynamic OG images in a Next.js application using a serverless function. We'll use the @vercel/og package to create the OG image within the serverless function.

Before we get started, you'll need to have a Next.js application set up. If you don't already have one, you can create a new Next.js project by running the following command:

bash

npx create-next-app

Install the @vercel/og package:

bash

npm install @vercel/og

Now that we have the necessary packages installed, we can create the serverless function that will generate the OG image. To do this, create a og.js in the api folder of your Next.js project and import ImageResponse class at the top of the file:

javascript

import { ImageResponse } from '@vercel/og';

Next, we'll set the runtime for the serverless function to "edge" in the config object:

javascript

export const config = {
  runtime: 'edge',
};

This will ensure that the function is executed on the edge network, which is required for creating OG images.

Now, let's define the main handler function for the serverless function:

javascript

export default function handler(req) {
  try {
    const { searchParams } = new URL(req.url);
    // code for generating the OG image goes here
  } catch (e) {
    return new Response(`Failed to generate the image`, {
      status: 500,
    });
  }
}

This function takes a request object as an argument and returns an OG image response. If an error occurs while generating the image, it returns a response with a status of 500 and an error message.

Now let's start adding the code for generating the OG image. First, we'll parse the search parameters from the request URL:

javascript
const { searchParams } = new URL(req.url);

Then, we'll check if the title search parameter is present in the query string:

javascript

const hasTitle = searchParams.has('title');

If the title parameter is present, we slice it to a maximum of 100 characters and assign it to the title variable, If the title parameter is not present, we set the title variable to the default value of "DevToMars":

javascript

const title = hasTitle
  ? searchParams.get('title')?.slice(0, 100)
  : 'DevToMars';

Next, we retrieve the mainTopics search parameter from the query string and split it into an array using the split method:

javascript

const mainTopics = searchParams.get('mainTopics').split(",")

This allows us to pass multiple main topics as a comma-separated list in the query string.

Now that we have the title and mainTopics variables, we can use them to render the OG image. To do this, we use the ImageResponse class from the @vercel/og package, which allows us to create an OG image from a React component.

We pass a simple div element with some inline styles applied to it to the ImageResponse class as the React component . The styles specify the layout and appearance of the OG image.:

jsx

return new ImageResponse(
      (
        <div
          style={{
            backgroundColor: 'black',
            backgroundSize: '150px 150px',
            height: '100%',
            width: '100%',
            display: 'flex',
            textAlign: 'center',
            alignItems: 'center',
            justifyContent: 'center',
            flexDirection: 'column',
            flexWrap: 'nowrap',
            padding: "65px",
          }}
        >
   {/* The Body of the Image */}
</div>

As you can see, we are displaying the div as a flex because Next.js requires this for every element with more than one child. You can use Tailwindcss for styling, but I don't recommend it because it is not stable and it is not fully supported for generating images with Next.js.

Our image will contain a title that fills up 75% of the image width with some padding and a margin-top of 30px, a logo with a brand name, and a list of main topic tags. :

jsx

...
<div style={{
                fontSize: 60,
                fontStyle: 'normal',
                letterSpacing: '-0.025em',
                color: 'white',
                marginTop: 30,
                padding: '0 100px',
                lineHeight: 1.4,
                whiteSpace: 'pre-wrap',
                height: "75%",
                fontWeight: 700,
                textTransform: "capitalize"
            }} className="w-10 capitalize">
                {title}
            </div>
            <div style={{ display: "flex", flexDirection: "row", gap: "10px", alignItems: "center" }} >
                <div style={{ width: "70px", height: "70px", display: "flex" }}>
                    <img 
                        src="https://media.graphassets.com/gncWvSEqRFaBSUPZGoTP"
                        width={70}
                        height={70}
                        alt="devtomars blog"
                    />
                </div>

                <div style={{
                    backgroundClip: "text",
                    color: "transparent",
                    backgroundImage: "linear-gradient(to right, #ec4899, #8b5cf6)",
                    fontWeight: 700,
                    fontSize: 30
                }} >DevToMars</div>
                

                <div style={{ display: "flex", flexDirection: "row", justifyContent: "flex-end", justifyItems: "center",width: "75%" }}>
                    {
                        mainTopics.map((topic, i) => (
                            <div key={i} style={{ color: "black",  fontSize: 20, padding: "5px", margin: "3px", backgroundColor: "white", borderRadius: "8px" }}>{topic}</div>
                        ))
                    }
                </div>
            </div>
...

Finally, we pass an option object to create an OG image with a width of 1200 pixels and a height of 630 pixels:

javascript

return new ImageResponse(
  // JSX element here
  {
    width: 1200,
    height: 630,
  },
);

And that's it! You now have a serverless function that can generate dynamic OG images for your Next.js application. To use the function, simply make a request to the function's URL with the desired title and mainTopics query parameters. For example:

javascript
https://your-next-app.vercel.app/api/og?title=My Blog Post&mainTopics=nodejs,javascript,react

This will generate an OG image with the title "My Blog Post" and the main topics "nodejs", "javascript", and "react".

You can then use the generated OG image in your social media sharing tags, like so:

jsx

<meta property="og:image" content="https://your-next-app.vercel.app/api/og?title=My Blog Post&mainTopics=nodejs,javascript,react" />

That's it! You now know how to create dynamic OG images for your Next.js application using a serverless function. I hope this tutorial was helpful!

Next.jsserverless functionsOG imagessocial media sharingdynamic content