Jul 6, 2023

How to set up Open Graph image for your Next.js project

Learn how to simply Open Graph image creation for your Next.js project by incorporating Stencil into your project.

What is Open Graph image and why do you need one?

The Open Graph image is a specific image associated with a webpage or a piece of content that is used when that content is shared on social media platforms like Facebook, Twitter, LinkedIn, and others. It is part of the Open Graph protocol, which allows websites to control how their content appears when shared.

Here are a few reasons why having an Open Graph image is important:

  1. Visual impression: When a link is shared on social media, the Open Graph image is displayed alongside the text description. An engaging and relevant image can capture attention, increase click-through rates, and encourage users to interact with the content.

  2. Branding and recognition: The Open Graph image can include visual elements related to your brand, such as a logo or specific colors. By consistently using a branded image across all shared content, you can reinforce your brand identity and make your content easily recognizable.

  3. Control over the preview: Without an Open Graph image, social media platforms may choose a random image from your webpage as the preview, which may not accurately represent the content. By specifying an Open Graph image, you have control over which image is used and can ensure it reflects the essence of your content.

  4. Shareability and virality: An attention-grabbing Open Graph image is more likely to be shared by users, increasing the potential for your content to go viral. Visual content is often more shareable, and a well-designed image can make your link stand out amidst the flood of other content on social media.

How to set up Open Graph image for Next.js project?

This tutorial demonstrates how to set up Open Graph image for your Next.js project using a sample blog written in Next.js. You can clone the sample blog repository to follow along.

Clone this repo to follow along

https://github.com/shadcn/next-contentlayer

Demonstrating the use of a blog is helpful as it showcases how to generate Open Graph images dynamically for each blog post. This approach allows for a unique image to be customized for each post.

Static image vs programmatically generated image

Next.js provides two ways of handling Open Graph images. The first approach is to serve a static image on a particular path specified by Next.js and Next.js will serve this image for all of your contents that match that route.

The second approach is to programmatically generate Open Graph image for each blog post. This allows for better content personalization that matches each blog post.

This tutorial will follow the second approach in generating Open Graph images. We will initially utilize the tools provided by Next.js, and later on explain how Stencil can assist in addressing some of the hurdles that you may face.

Our goal is to create an Open Graph image that contains the title of the post and other design elements.

1. Setting up our project

If you have not cloned the repo yet, please do so. First, we need to create a opengraph-image.js file containing the necessary function for generating the Open Graph image programmatically as suggested in the Next.js official documentation.

JAVASCRIPT
import { ImageResponse } from 'next/server' // Route segment config export const runtime = 'edge' // Image metadata export const alt = 'About Acme' export const size = { width: 1200, height: 630, } // Font const interSemiBold = fetch( new URL('./Inter-SemiBold.ttf', import.meta.url) ).then((res) => res.arrayBuffer()) export const contentType = 'image/png' // Image generation export default function Image() { return new ImageResponse( ( // ImageResponse JSX element <div style={{ fontSize: 128, background: 'white', width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', }} > About Acme </div> ), // ImageResponse options { // For convenience, we can re-use the exported Open Graph-image // size config to also set the ImageResponse's width and height. ...size, fonts: [ { name: 'Inter', data: await interSemiBold, style: 'normal', weight: 400, }, ], } ) }

For the sample blog, the opengraph-image.js file should be located in app/posts/[...slug]/openraph-image.js, given that each blog post requires its unique Open Graph image. If you try to run this by running npm run dev, Next.js will throw an error.

That's a bummer!

It is currently not feasible because of a bug in Next.js. You can view the whole discussion here https://github.com/vercel/next.js/issues/48162. A workaround is provided.

To solve this, we have to create an indirection to generate this image instead of letting Next.js handles this for us. It's a bit more code but this is the only way to do it for now until the bug is fixed.

2. Create Open Graph handler

The idea behind the workaround is to create a new handler that generates the Open Graph image for us at a specified path. Then, we need to call this handler from somewhere in order to create the image.

We can do that by exporting generateMetadata function in app/posts/[...slug]/page.tsx that returns our modified metadata. The metadata will contain the URL to the handler, so that whenever an Open Graph image is requested for a particular path, the Open Graph handler is called.

To create the handler, first, we need to remove app/posts/[...slug]/opengraph-image.js that we added in the previous step. This should make our project compiling again.

Then, we are going to create a route handler at app/opengraph/[...path]/route.js. Add the following content to the file, essentially it does the same thing as our original opengraph-image.js but modified to match the convention for a route handler.

JAVASCRIPT
import { allPosts } from "contentlayer/generated" import { ImageResponse } from 'next/server' // Route segment config export const runtime = 'edge' // Image metadata export const alt = 'About Acme' export const size = { width: 1200, height: 630, } export const contentType = 'image/png' export async function GET(request, { params }) { // Find relevant post that matches the slug provided in `params` const slug = params.path.join("/") const post = allPosts.find((post) => post.slugAsParams === slug) // Create an image with the title of the post return new ImageResponse( ( // ImageResponse JSX element <div style={{ fontSize: 128, background: 'white', width: '100%', height: '100%', display: 'flex', alignItems: 'center', justifyContent: 'center', }} > {post.title} </div> ) ) }

To test everything is working at this point, you can go to [http://localhost:3000/Open Graph/deploying-next-apps](http://localhost:3000/Open Graph/deploying-next-apps). If everything's working, you should see an image that looks like this.

3. Adjust page handler to return proper metadata

Now that we have confirmed that we can generate Open Graph image from the Open Graph handler that was created in the previous step, we can set the page handler for the blog post to return proper metadata that includes this programmatically generatedd Open Graph image.

To do that, we need to make some adjustment to app/posts/[...slug]/page.tsx.

JAVASCRIPT
export async function generateMetadata({ params, }: PostProps): Promise<Metadata> { const post = await getPostFromParams(params) if (!post) { return {} } const slug = params?.slug?.join("/") const title = post.title return { title: post.title, description: post.description, openGraph: { title: post.title, description: post.description, images: [ { type: "image/png", width: 1200, height: 630, url: `/opengraph/${slug}` } ] } } }

Next.js specifies that we can return a function called generateMetadata to override the metadata for each of the relevant blog posts. Here we specify the URL for the Open Graph image. If you notice, the URL for this image is actually our Open Graph handler. Our handler returns an image response, so this should work.

4. Design a better template

Unfortunately, the generated Open Graph image that we have produced so far may not be very visually appealing. To find inspiration and improve the image, there are various Open Graph templates available on the internet that you can refer to.

Do take note, however, that for the Next.js ImageResponse, you will need to pass in a React component that Next.js will help to render for you. While this is helpful, there are still limitations on how you can generate the image because not all HTML/CSS features are supported.

Most of the time, the available supported features are more than enough to handle a simple design for Open Graph image. However, because there is no visual editor to work with, which means that good design will have to be manually coded and tested to ensure that it matches your intended outcome.

You can read what options are available to use when writing your React component to build the image, https://nextjs.org/docs/app/api-reference/functions/image-response#supported-css-properties

Simplifying things with Stencil

Implementing the Open Graph image was a fun experience. The Next.js documentation was effective in directing us to the right solution. Despite this, there are still some pain points to consider:

  1. Creating an aesthetically pleasing Open Graph image can be challenging without a visual editor.

  2. Creating the indirection necessary to make the Open Graph image programmatically functional requires some searching and effort.

Stencil can solve both of these paint points. If you're following along, you can clone the repo again to start from scratch and compare both of these approaches.

1. Design Open Graph image using Stencil's template editor

To begin, create a new project and template. If you don't have a Stencil account yet, registration is free and comes with 50 credits that can be used for 50 image generations.

What is the standard size for Open Graph image

As for the standard size for Open Graph images, the recommended dimensions are typically 1200x630. However, services such as Twitter may have different standards.

Our template editor offers a range of layer components at your disposal, including but not limited to custom font options, QR code generation, and additional features not offered by Next.js' built-in Open Graph image support. Most importantly, the visual editor allows for a "what you see is what you get" (WYSIWYG) experience, thus eliminating the need to write code from scratch and saving you time.

Drag-and-drop template editor with various design choices

To start, create a new project and then create a new template. If you don't have an account with Stencil, you can register for free and there is 50 credits available for you to try on that is good for 50 image generations.

2. Test your design

If you're satisfied with your design, you can test it using our easy to use Test Console. Head over to your project, and click on "Console".

Test API Console for quickly testing your changes

To the left of the screen, you'll find a table outlining the various modifications that can be made to the existing template.

This table can serve as a handy reference to see what fields are accessible for editing.

To the right of the screen, you'll find a JSON editor where you can enter your desired modifications. From the dropdown menu, you can select whether you want to generate an image or a PDF file.

For the purposes of this tutorial, we will apply the following modifications to the template. If you are following along, you can copy and paste the provided snippet, ensuring that you adjust the template ID accordingly to match your selected template.

JSON
{ "template": "cc717ef4-f16a-4360-b864-3e28dbe8ef27", "modifications": [ { "name": "image", "src": "https://images.unsplash.com/photo-1614741118887-7a4ee193a5fa" }, { "name": "title", "text": "How to set up Open Graph image for your Next.js project" }, { "name": "author", "text": "SHULHI SAPLI" } ] }

Once you have made the necessary modifications and are satisfied with the results, simply click "Generate". After a few seconds, you should see the result in the output pane below.

If everything seems good, you can proceed to the next step. Otherwise, make more adjustment to your template.

3. Generate query string URL for your template

Head over to you project one more time, and click on the three dots (Kebab menu icon) on the top right of your template. Select "Image URLs".

Here you need to add a new query string parameter that maps to your intended modification. In our case, we would like to create a new query string that will modify the title of our Open Graph template.

We've assigned query string 't' to the 'title'

Now, whenever you go to this link, it will generate an image with the specified title from the query string parameter.

Stencil is smart enough to figure out whether if you have generated the same image as before, if you have, it will pull the image from cache making the image load very fast. We also utilize Amazon Cloudfront CDN, so you don't have to worry about image loading time.

The first image generation takes around 2-3s and your credit is only deducted for the first image generation.

TIPS

While on average image generation takes 2-3s, it also depends on other factors like how many image layers are used in your template. When you specify image layers with larger size, it takes time to download them and may slow down image generation process.

Securing access to your query string

It's worth noting that anyone who can view the image will have the ability to copy the link and adjust the parameter to generate a new image. Unfortunately, this is the nature of how query strings work.

Fortunately, there are two solutions available to tackle this issue:

1. Signed Images

Stencil has an additional feature available for creating signed images.

2. Setting Allowed Origins

On the Query String Integration page that we previously referenced, it's possible to set the allowed origin to your whitelisted domains. If a domain isn't on the list, it will not be able to access the image. This is particularly helpful if you only want to serve the image from your domain – which is usually the case for Open Graph.

This image can only be rendered from usestencil.com domain

It's essential to keep in mind that this solution isn't entirely secure because determined users can spoof the origin header. However, in our experience, it serves as an effective enough deterrent without requiring a more complicated approach like Signed Images.

4. Adjust page handler to return proper metadata

Now in our page handler, app/posts/[...slug]/page.tsx, we can update the generateMetadata function to use our query string URL instead of the Open Graph handler that we created previously. It should look like this.

JAVASCRIPT
export async function generateMetadata({ params, }: PostProps): Promise<Metadata> { const post = await getPostFromParams(params) if (!post) { return {} } const slug = params?.slug?.join("/") const title = post.title return { title: post.title, description: post.description, openGraph: { title: post.title, description: post.description, images: [ { type: "image/png", width: 1200, height: 630, url: `https://images.usestencil.com/qs/../...png?t=${title} ` } ] } } }

We no longer need the Open Graph handler, and it can be removed.

Making changes to your template

Using Stencil to create an Open Graph image offers an additional benefit: you can avoid modifying any code whenever you want to make adjustments to your template. This eliminates the need to redeploy your application.

Instead, you can easily make changes to the template in Stencil using the convenient template editor.

After making changes, you can invalidate Stencil's cache. This ensures that whenever someone shares the link to your page, a fresh image will be generated and cached. However, please note that you may still need to wait for the CDN (Content Delivery Network) to invalidate its cache for the changes to take effect.

Simply click on Invalidate button to invalidate Stencil's cache

We hope that this guide is helpful to simplify your Next.js project.

Ready to dive in?Start your free trial today.