Abdessamad Ely Logo

How to add OpenGraph to your Next.js website

Abdessamad Ely
Abdessamad Ely
Software Engineer
Reading time: 7 min read   •   Published on in Next.js   •   Updated on

In this tutorial, you will learn everything you need to know regarding Open Graph basics and how to integrate it on your Next.js website.

I will start by creating a new Next.js project using its default options with TailwindCSS and TypeScript, and that's with npx create-next-app@latest.

Once the project is ready, open it in your favourite code editor, then start the development server with npm run dev.

About OpenGraph

Before we touch any code, let's first learn a little bit about Open Graph and its properties, which we'll be adding to our Next.js website.

What is OpenGraph?

Open Graph is a set of meta tags that help social media platforms better understand our web pages.

This gives us control over how platforms like Facebook, LinkedIn, and X display previews of our pages when they’re shared in the feed.

When a web page is shared on social media, if Open Graph metadata isn’t present, platforms will fall back to whatever is available—such as the first image or text they find.

What are OpenGraph meta tags?

In this tutorial, we will add to a new Next.js website, the basic Open Graph meta tags for it to be considered valid:

oopen-graph-meta-tags.html
<meta property="og:title" content="page title" />
<meta property="og:description" content="short description" />
<meta property="og:type" content="page type" />
<meta property="og:url" content="page canonical url" />

<meta property="og:image" content="image-url" />
<meta property="og:image:alt" content="image-alt" />
<meta property="og:image:type" content="image-MIME-type" />
<meta property="og:image:width" content="image-width" />
<meta property="og:image:height" content="image-height" />

To learn more about available Open Graph meta tags and their possible values, you can always visit the official ogp.me website.

For example, the og:type meta tag has a set of predefined values, but in our case, we’ll use the website and article types.

Open Graph Next.js project

Now that we have a basic understanding of Open Graph, let’s create a new Next.js project and set up some basic pages.

Please skip this section if you already have a project, otherwise go ahead and create a fresh Next.js project then go to the next section.

To make it easy, we'll use an example of a blog, where we have different type of pages like:

  • Landing page
  • Listing page
  • Detail page

Let's add Open Graph to each one of them.

Add Open Graph to a landing page

Mostly, for landing pages, we have a static image that represents the brand, so let's consider the image is stored at: /public/og-image-home.jpg.

Open graph images should respect the recommend aspect ratio 1.91:1, something like 1200x630 or 600x315, to avoid being cropped.

For example, for my website's homepage I use this 600x315 image:

Open Graph image for Abdessamad Ely

Let's update our homepage then explain how it works:

app/page.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Landing page for Open Graph tutorial',
  description: 'Learn about how to add OpenGraph to your Next.js website',
  openGraph: {
    title: 'Landing page for Open Graph tutorial',
    description: 'Learn about how to add OpenGraph to your Next.js website',
    type: 'website',
    url: process.env.APP_URL || 'http://localhost/',
    images: {
      url: `${process.env.APP_URL || 'http://localhost/'}/og-image-home.jpg`,
      alt: 'Open Graph image for Abdessamad Ely Homepage',
      type: 'image/jpeg',
      width: 600,
      height: 315,
    },
  },
}

export default function Home() {
  return (
    <main>
      <h1>Landing page for Open Graph tutorial</h1>
      <p>Learn about how to add OpenGraph to your Next.js website</p>
    </main>
  )
}

Using Next.js static metadata, we export the metadata object with an openGraph attribute, which defines our page’s Open Graph metadata.

You can also use Next.js Metadata files convention, by putting opengraph-image.jpg image file next to your page.tsx.

Learn more about metadata conventions for Open Graph images.

We also used process.env.APP_URL to access the configured APP_URL from our .env file, so make sure it’s defined, as OG tags works with absolute URLs.

.env
APP_URL=https://abdessamadely.com/

Now, if you visit your website’s landing page, you should be able to inspect and check the added meta tags.

Inspecting Open Graph meta tags

We can also add Open Graph to any Next.js static page following the same approach as the homepage.

Add Open Graph to a listing page

While the listing page is not static in terms of the content, it is considered static in terms of metadata, as its canonical URL, title, and description don’t change.

Please apply the same approach as in the landing page section above.

Add Open Graph to a detail page

For detail page, we will use the following data/posts.json file as our dynamic content:

data/posts.json
[
  {
    "id": 1,
    "title": "Post 1",
    "slug": "post-1",
    "thumbnail": "/images/og-posts-image.jpg",
    "description": "Description of post 1",
    "content": "Content of post 1"
  },
  {
    "id": 2,
    "title": "Post 2",
    "slug": "post-2",
    "thumbnail": "/images/og-posts-image.jpg",
    "description": "Description of post 2",
    "content": "Content of post 2"
  },
  {
    "id": 3,
    "title": "Post 3",
    "slug": "post-3",
    "description": "Description of post 3",
    "content": "Content of post 3"
  }
]

We have a list of 3 posts, 2 posts have a thumbnail image which can be used for the og:image, but the last post doesn't have an image.

This way, we will learn about using dynamically generated metadata, as well as generating Open Graph images on the fly with Next.js ImageResponse.

Set up a post detail page

Let's first have a working detail page based on our posts.json file:

app/posts/[slug]/page.tsx
import type { Metadata } from 'next'
import posts from '@/data/posts.json'
import { notFound } from 'next/navigation'

type Props = {
  params: Promise<{ slug: string }>
}

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const slug = (await params).slug
  const post = posts.find((p) => p.slug === slug)

  if (!post) {
    return {}
  }

  return {
    title: post.title,
    description: post.description,
    openGraph: {
      title: post.title,
      description: post.description,
      type: 'article',
      url: new URL('/posts/' + post.slug, process.env.APP_URL).toString(),
      images: {
        url: post.thumbnail
          ? new URL(post.thumbnail, process.env.APP_URL).toString()
          : new URL(
              `/posts/${post.slug}/opengraph-image`,
              process.env.APP_URL
            ).toString(),
        alt: `Open Graph image for ${post.title}`,
        type: 'image/jpeg',
        width: 600,
        height: 315,
      },
    },
  }
}

export default async function Post({ params }: Props) {
  const slug = (await params).slug
  const post = posts.find((p) => p.slug === slug)

  if (!post) {
    notFound()
  }

  return (
    <main>
      <h1>{post.title}</h1>
      {post.thumbnail && <div>Post with thumbnail: {post.thumbnail}</div>}
      <p>{post.description}</p>
      <article dangerouslySetInnerHTML={{ __html: post.content }}></article>
    </main>
  )
}

We've added a simple post detail page, which lookup a post by it's slug, and show the post details.

We also added dynamic metadata by exporting the Next.js generateMetadata function, then we use the post information to set the page’s metadata.

When the post have a thumbnail we manually set the og:image properties, if not we just set it to undefined so we can dynamically generate it.

Creating a OpenGraph Image page

Let's start by creating a new file next to our post detail page at app/posts/[slug]/opengraph-image.tsx:

app/posts/[slug]/opengraph-image.tsx
import posts from '@/data/posts.json'
import { ImageResponse } from 'next/og'
import { notFound } from 'next/navigation'

export const contentType = 'image/png'

export default async function Image({ params }: { params: { slug: string } }) {
  const slug = (await params).slug
  const post = posts.find((p) => p.slug === slug)

  if (!post || post.thumbnail) {
    notFound()
  }

  return new ImageResponse(
    (
      <div
        style={{
          gap: 12,
          width: '100%',
          height: '100%',
          padding: 34,
          display: 'flex',
          background: 'white',
          flexDirection: 'column',
          justifyContent: 'center',
        }}
      >
        <div
          style={{
            fontSize: 48,
            gap: 12,
            display: 'flex',
            alignItems: 'center',
            fontWeight: 'bold',
          }}
        >
          <svg
            xmlns="http://www.w3.org/2000/svg"
            fill="none"
            viewBox="0 0 24 24"
            width={48}
            height={48}
            stroke-width="1.5"
            stroke="currentColor"
          >
            <path
              stroke-linecap="round"
              stroke-linejoin="round"
              d="M4.26 10.147a60.438 60.438 0 0 0-.491 6.347A48.62 48.62 0 0 1 12 20.904a48.62 48.62 0 0 1 8.232-4.41 60.46 60.46 0 0 0-.491-6.347m-15.482 0a50.636 50.636 0 0 0-2.658-.813A59.906 59.906 0 0 1 12 3.493a59.903 59.903 0 0 1 10.399 5.84c-.896.248-1.783.52-2.658.814m-15.482 0A50.717 50.717 0 0 1 12 13.489a50.702 50.702 0 0 1 7.74-3.342M6.75 15a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm0 0v-3.675A55.378 55.378 0 0 1 12 8.443m-7.007 11.55A5.981 5.981 0 0 0 6.75 15.75v-1.5"
            />
          </svg>
          <span>{post.title}</span>
        </div>
        <div
          style={{
            fontSize: 18,
          }}
        >
          {post.description}
        </div>
      </div>
    ),
    {
      width: 600,
      height: 315,
    }
  )
}

The opengraph-image.tsx filename, is special for generating og images, you can also use twitter-image.tsx if you want to have different image for Twitter/X.

With the help of Next.js ImageResponse its simple to create an OG Image template from combining HTML, SVG and CSS.

We also export the contentType so that browsers correctly render it as an image.

Now, if you go to the /posts/post-3/opengraph-image page, you should see an image with an icon, title, and description.

Also if you inspect one of the post detail pages, you will see that Open Graph meta tags were added correctly.

Conclusion

This tutorial, covered how you can add OpenGraph meta tags to a Next.js website, we focused mainly on two type of pages.

A page with static metadata, and a page with dynamically generated metadata, with a practical example of a simple blog.