Skip to main content

opengraph-image and twitter-image

The opengraph-image and twitter-image file conventions allow you to set Open Graph and Twitter images for a route segment.

They are useful for setting the images that appear on social networks and messaging apps when a user shares a link to your site.

There are two ways to set Open Graph and Twitter images:

Image files (.jpg, .png, .gif)​

Use an image file to set a route segment's shared image by placing an opengraph-image or twitter-image image file in the segment.

Next.js will evaluate the file and automatically add the appropriate tags to your app's <head> element.

File conventionSupported file types
opengraph-image.jpg, .jpeg, .png, .gif
twitter-image.jpg, .jpeg, .png, .gif
opengraph-image.alt.txt
twitter-image.alt.txt

opengraph-image​

Add an opengraph-image.(jpg|jpeg|png|gif) image file to any route segment.

<meta property="og:image" content="<generated>" />
<meta property="og:image:type" content="<generated>" />
<meta property="og:image:width" content="<generated>" />
<meta property="og:image:height" content="<generated>" />

twitter-image​

Add a twitter-image.(jpg|jpeg|png|gif) image file to any route segment.

<meta name="twitter:image" content="<generated>" />
<meta name="twitter:image:type" content="<generated>" />
<meta name="twitter:image:width" content="<generated>" />
<meta name="twitter:image:height" content="<generated>" />

opengraph-image.alt.txt​

Add an accompanying opengraph-image.alt.txt file in the same route segment as the opengraph-image.(jpg|jpeg|png|gif) image it's alt text.

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

twitter-image.alt.txt​

Add an accompanying twitter-image.alt.txt file in the same route segment as the twitter-image.(jpg|jpeg|png|gif) image it's alt text.

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

Generate images using code (.js, .ts, .tsx)​

In addition to using literal image files, you can programmatically generate images using code.

Generate a route segment's shared image by creating an opengraph-image or twitter-image route that default exports a function.

File conventionSupported file types
opengraph-image.js, .ts, .tsx
twitter-image.js, .ts, .tsx

Good to know

The easiest way to generate an image is to use the ImageResponse API from next/og.

import { ImageResponse } from 'next/og'

export const runtime = 'edge'

// Image metadata
export const alt = 'About Acme'
export const size = {
width: 1200,
height: 630,
}

export const contentType = 'image/png'

// Image generation
export default async function Image() {
// Font
const interSemiBold = fetch(
new URL('./Inter-SemiBold.ttf', import.meta.url)
).then((res) => res.arrayBuffer())

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 opengraph-image
// size config to also set the ImageResponse's width and height.
...size,
fonts: [
{
name: 'Inter',
data: await interSemiBold,
style: 'normal',
weight: 400,
},
],
}
)
}
import { ImageResponse } from 'next/og'

export const runtime = 'edge'

// Image metadata
export const alt = 'About Acme'
export const size = {
width: 1200,
height: 630,
}

export const contentType = 'image/png'

// Image generation
export default async function Image() {
// Font
const interSemiBold = fetch(
new URL('./Inter-SemiBold.ttf', import.meta.url)
).then((res) => res.arrayBuffer())

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 opengraph-image
// size config to also set the ImageResponse's width and height.
...size,
fonts: [
{
name: 'Inter',
data: await interSemiBold,
style: 'normal',
weight: 400,
},
],
}
)
}
<meta property="og:image" content="<generated>" />
<meta property="og:image:alt" content="About Acme" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />

Props​

The default export function receives the following props:

params (optional)​

An object containing the dynamic route parameters object from the root segment down to the segment opengraph-image or twitter-image is colocated in.

export default function Image({ params }: { params: { slug: string } }) {
// ...
}
export default function Image({ params }) {
// ...
}
RouteURLparams
app/shop/opengraph-image.js/shopundefined
app/shop/[slug]/opengraph-image.js/shop/1{ slug: '1' }
app/shop/[tag]/[item]/opengraph-image.js/shop/1/2{ tag: '1', item: '2' }
app/shop/[...slug]/opengraph-image.js/shop/1/2{ slug: ['1', '2'] }

Returns​

The default export function should return a Blob | ArrayBuffer | TypedArray | DataView | ReadableStream | Response.

Good to know: ImageResponse satisfies this return type.

Config exports​

You can optionally configure the image's metadata by exporting alt, size, and contentType variables from opengraph-image or twitter-image route.

OptionType
altstring
size{ width: number; height: number }
contentTypestring - image MIME type

alt​

export const alt = 'My images alt text'

export default function Image() {}
export const alt = 'My images alt text'

export default function Image() {}
<meta property="og:image:alt" content="My images alt text" />

size​

export const size = { width: 1200, height: 630 }

export default function Image() {}
export const size = { width: 1200, height: 630 }

export default function Image() {}
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />

contentType​

export const contentType = 'image/png'

export default function Image() {}
export const contentType = 'image/png'

export default function Image() {}
<meta property="og:image:type" content="image/png" />

Route Segment Config​

opengraph-image and twitter-image are specialized Route Handlers that can use the same route segment configuration options as Pages and Layouts.

Examples​

Using external data​

This example uses the params object and external data to generate the image.

Good to know: By default, this generated image will be statically optimized. You can configure the individual fetch options or route segments options to change this behavior.

import { ImageResponse } from 'next/og'

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

export default async function Image({ params }: { params: { slug: string } }) {
const post = await fetch(`https://.../posts/${params.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>
),
{
...size,
}
)
}
import { ImageResponse } from 'next/og'

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

export default async function Image({ params }) {
const post = await fetch(`https://.../posts/${params.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>
),
{
...size,
}
)
}

Using Edge runtime with local assets​

This example uses the Edge runtime to fetch a local image on the file system and passes it as an ArrayBuffer to the src attribute of an <img> element. The local asset should be placed relative to the example source file location.

import { ImageResponse } from 'next/og'

export const runtime = 'edge'

export default async function Image() {
const logoSrc = await fetch(new URL('./logo.png', import.meta.url)).then(
(res) => res.arrayBuffer()
)

return new ImageResponse(
(
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<img src={logoSrc} height="100" />
</div>
)
)
}
import { ImageResponse } from 'next/og'

export const runtime = 'edge'

export default async function Image() {
const logoSrc = await fetch(new URL('./logo.png', import.meta.url)).then(
(res) => res.arrayBuffer()
)

return new ImageResponse(
(
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<img src={logoSrc} height="100" />
</div>
)
)
}

Using Node.js runtime with local assets​

This example uses the Node.js runtime to fetch a local image on the file system and passes it as an ArrayBuffer to the src attribute of an <img> element. The local asset should be placed relative to the root of your project, rather than the location of the example source file.

import { ImageResponse } from 'next/og'
import { join } from 'node:path'
import { readFile } from 'node:fs/promises'

export default async function Image() {
const logoData = await readFile(join(process.cwd(), 'logo.png'))
const logoSrc = Uint8Array.from(logoData).buffer

return new ImageResponse(
(
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<img src={logoSrc} height="100" />
</div>
)
)
}
import { ImageResponse } from 'next/og'
import { join } from 'node:path'
import { readFile } from 'node:fs/promises'

export default async function Image() {
const logoData = await readFile(join(process.cwd(), 'logo.png'))
const logoSrc = Uint8Array.from(logoData).buffer

return new ImageResponse(
(
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<img src={logoSrc} height="100" />
</div>
)
)
}

Version History​

VersionChanges
v13.3.0opengraph-image and twitter-image introduced.