opengraph-image and twitter-image in Next.js — Generating Social Preview Images for Each Route

Next.js supports special conventions for generating social preview images using opengraph-image and twitter-image files. These images appear when your site is shared on platforms like Twitter, WhatsApp, or LinkedIn. You can define them using static image files or generate them dynamically with ImageResponse. This article explains both approaches, metadata configuration, and how to customize previews per route segment.

opengraph-imagetwitter-imageImageResponsemeta tags

~3 min read • Updated Oct 29, 2025

1. What Are opengraph-image and twitter-image?


These files define the preview image shown when a link to your site is shared on social media or messaging platforms. The browser uses metadata in the <head> to display the image.


2. Ways to Define Images


You can define social images in two ways:

  1. Using image files (.jpg, .png, .gif)
  2. Generating images with code (.js, .ts, .tsx)

3. Using Image Files


Place the image in the route segment:

  • opengraph-image.jpg → for Open Graph
  • twitter-image.png → for Twitter
  • opengraph-image.alt.txt → alt text for Open Graph
  • twitter-image.alt.txt → alt text for Twitter

Size Limits:

  • twitter-image: max 5MB
  • opengraph-image: max 8MB

Example <head> Output:

<meta property="og:image" content="..." />
<meta name="twitter:image" content="..." />
<meta property="og:image:alt" content="About Acme" />
<meta name="twitter:image:alt" content="About Acme" />

4. Generating Images with Code


Use the ImageResponse API from next/og:

// app/about/opengraph-image.tsx
import { ImageResponse } from 'next/og'
import { readFile } from 'node:fs/promises'
import { join } from 'node:path'

export const alt = 'About Acme'
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'

export default async function Image() {
  const font = await readFile(join(process.cwd(), 'assets/Inter-SemiBold.ttf'))

  return new ImageResponse(
    <div style={{
      fontSize: 128,
      background: 'white',
      width: '100%',
      height: '100%',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
    }}>
      About Acme
    </div>,
    {
      ...size,
      fonts: [{ name: 'Inter', data: font, style: 'normal', weight: 400 }],
    }
  )
}

5. Input and Output Parameters


The export function can receive dynamic route params:

export default async function Image({ params }) {
  const { slug } = await params
}

Valid return types include:

  • Blob
  • ArrayBuffer
  • TypedArray
  • ReadableStream
  • Response

6. Metadata Configuration


You can export metadata for the image:

export const alt = 'My images alt text'
export const size = { width: 1200, height: 630 }
export const contentType = 'image/png'

7. Example with External Data


Generate the image based on API data:

// app/posts/[slug]/opengraph-image.tsx
export default async function Image({ params }) {
  const { slug } = await params
  const post = await fetch(`https://.../posts/${slug}`).then(res => res.json())

  return new ImageResponse(
    <div style={{
      fontSize: 48,
      background: 'white',
      width: '100%',
      height: '100%',
      display: 'flex',
      alignItems: 'center',
      justifyContent: 'center',
    }}>
      {post.title}
    </div>,
    { width: 1200, height: 630 }
  )
}

8. Using Local Assets


Read a local image and convert to base64:

const logoData = await readFile(join(process.cwd(), 'logo.png'), 'base64')
const logoSrc = `data:image/png;base64,${logoData}`

Conclusion


With opengraph-image and twitter-image in Next.js, you can create rich, customizable social previews for every route. Whether using static files or dynamic code, these features give you full control over how your links appear when shared.


Written & researched by Dr. Shahin Siami