---
title: Metadata and OG images
description: Learn how to add metadata to your pages and create dynamic OG images.
related:
title: API Reference
description: Learn more about the Metadata APIs mentioned in this page.
links:
- app/api-reference/functions/generate-metadata
- app/api-reference/functions/generate-viewport
- app/api-reference/functions/image-response
- app/api-reference/file-conventions/metadata
- app/api-reference/file-conventions/metadata/app-icons
- app/api-reference/file-conventions/metadata/opengraph-image
- app/api-reference/file-conventions/metadata/robots
- app/api-reference/file-conventions/metadata/sitemap
- app/api-reference/config/next-config-js/htmlLimitedBots
---
The Metadata APIs can be used to define your application metadata for improved SEO and web shareability and include:
1. [The static `metadata` object](#static-metadata)
2. [The dynamic `generateMetadata` function](#generated-metadata)
3. Special [file conventions](/docs/app/api-reference/file-conventions/metadata) that can be used to add static or dynamically generated [favicons](#favicons) and [OG images](#static-open-graph-images).
With all the options above, Next.js will automatically generate the relevant `<head>` tags for your page, which can be inspected in the browser's developer tools.
The `metadata` object and `generateMetadata` function exports are only supported in Server Components.
## Default fields
There are two default `meta` tags that are always added even if a route doesn't define metadata:
- The [meta charset tag](https://developer.mozilla.org/docs/Web/HTML/Element/meta#attr-charset) sets the character encoding for the website.
- The [meta viewport tag](https://developer.mozilla.org/docs/Web/HTML/Viewport_meta_tag) sets the viewport width and scale for the website to adjust for different devices.
```html
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
```
The other metadata fields can be defined with the `Metadata` object (for [static metadata](#static-metadata)) or the `generateMetadata` function (for [generated metadata](#generated-metadata)).
## Static metadata
To define static metadata, export a [`Metadata` object](/docs/app/api-reference/functions/generate-metadata#metadata-object) from a static [`layout.js`](/docs/app/api-reference/file-conventions/layout) or [`page.js`](/docs/app/api-reference/file-conventions/page) file. For example, to add a title and description to the blog route:
```tsx filename="app/blog/layout.tsx" switcher
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'My Blog',
description: '...',
}
export default function Layout() {}
```
```jsx filename="app/blog/layout.js" switcher
export const metadata = {
title: 'My Blog',
description: '...',
}
export default function Layout() {}
```
You can view a full list of available options, in the [`generateMetadata` documentation](/docs/app/api-reference/functions/generate-metadata#metadata-fields).
## Generated metadata
You can use [`generateMetadata`](/docs/app/api-reference/functions/generate-metadata) function to `fetch` metadata that depends on data. For example, to fetch the title and description for a specific blog post:
```tsx filename="app/blog/[slug]/page.tsx" switcher
import type { Metadata, ResolvingMetadata } from 'next'
type Props = {
params: Promise<{ slug: string }>
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}
export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
const slug = (await params).slug
// fetch post information
const post = await fetch(`https://api.vercel.app/blog/${slug}`).then((res) =>
res.json()
)
return {
title: post.title,
description: post.description,
}
}
export default function Page({ params, searchParams }: Props) {}
```
```jsx filename="app/blog/[slug]/page.js" switcher
export async function generateMetadata({ params, searchParams }, parent) {
const slug = (await params).slug
// fetch post information
const post = await fetch(`https://api.vercel.app/blog/${slug}`).then((res) =>
res.json()
)
return {
title: post.title,
description: post.description,
}
}
export default function Page({ params, searchParams }) {}
```
### Streaming metadata
For dynamically rendered pages, Next.js streams metadata separately, injecting it into the HTML once `generateMetadata` resolves, without blocking UI rendering.
Streaming metadata improves perceived performance by allowing visual content to stream first.
Streaming metadata is **disabled for bots and crawlers** that expect metadata to be in the `<head>` tag (e.g. `Twitterbot`, `Slackbot`, `Bingbot`). These are detected by using the User Agent header from the incoming request.
You can customize or **disable** streaming metadata completely, with the [`htmlLimitedBots`](/docs/app/api-reference/config/next-config-js/htmlLimitedBots#disabling) option in your Next.js config file.
Prerendered pages don’t use streaming since metadata is resolved at build time.
Learn more about [streaming metadata](/docs/app/api-reference/functions/generate-metadata#streaming-metadata).
### Memoizing data requests
There may be cases where you need to fetch the **same** data for metadata and the page itself. To avoid duplicate requests, you can use React's [`cache` function](https://react.dev/reference/react/cache) to memoize the return value and only fetch the data once. For example, to fetch the blog post information for both the metadata and the page:
```ts filename="app/lib/data.ts" highlight={5} switcher
import { cache } from 'react'
import { db } from '@/app/lib/db'
// getPost will be used twice, but execute only once
export const getPost = cache(async (slug: string) => {
const res = await db.query.posts.findFirst({ where: eq(posts.slug, slug) })
return res
})
```
```js filename="app/lib/data.js" highlight={5} switcher
import { cache } from 'react'
import { db } from '@/app/lib/db'
// getPost will be used twice, but execute only once
export const getPost = cache(async (slug) => {
const res = await db.query.posts.findFirst({ where: eq(posts.slug, slug) })
return res
})
```
```tsx filename="app/blog/[slug]/page.tsx" switcher
import { getPost } from '@/app/lib/data'
export async function generateMetadata({
params,
}: {
params: { slug: string }
}) {
const post = await getPost(params.slug)
return {
title: post.title,
description: post.description,
}
}
export default async function Page({ params }: { params: { slug: string } }) {
const post = await getPost(params.slug)
return <div>{post.title}</div>
}
```
```jsx filename="app/blog/[slug]/page.js" switcher
import { getPost } from '@/app/lib/data'
export async function generateMetadata({ params }) {
const post = await getPost(params.slug)
return {
title: post.title,
description: post.description,
}
}
export default async function Page({ params }) {
const post = await getPost(params.slug)
return <div>{post.title}</div>
}
```
## File-based metadata
The following special files are available for metadata:
- [favicon.ico, apple-icon.jpg, and icon.jpg](/docs/app/api-reference/file-conventions/metadata/app-icons)
- [opengraph-image.jpg and twitter-image.jpg](/docs/app/api-reference/file-conventions/metadata/opengraph-image)
- [robots.txt](/docs/app/api-reference/file-conventions/metadata/robots)
- [sitemap.xml](/docs/app/api-reference/file-conventions/metadata/sitemap)
You can use these for static metadata, or you can programmatically generate these files with code.
## Favicons
Favicons are small icons that represent your site in bookmarks and search results. To add a favicon to your application, create a `favicon.ico` and add to the root of the app folder.
<Image
alt="Favicon Special File inside the App Folder with sibling layout and page files"
srcLight="/docs/light/favicon-ico.png"
srcDark="/docs/dark/favicon-ico.png"
width="1600"
height="444"
/>
> You can also programmatically generate favicons using code. See the [favicon docs](/docs/app/api-reference/file-conventions/metadata/app-icons) for more information.
## Static Open Graph images
Open Graph (OG) images are images that represent your site in social media. To add a static OG image to your application, create a `opengraph-image.jpg` file in the root of the app folder.
<Image
alt="OG image special file inside the App folder with sibling layout and page files"
srcLight="/docs/light/opengraph-image.png"
srcDark="/docs/dark/opengraph-image.png"
width="1600"
height="444"
/>
You can also add OG images for specific routes by creating a `opengraph-image.jpg` deeper down the folder structure. For example, to create an OG image specific to the `/blog` route, add a `opengraph-image.jpg` file inside the `blog` folder.
<Image
alt="OG image special file inside the blog folder"
srcLight="/docs/light/opengraph-image-blog.png"
srcDark="/docs/dark/opengraph-image-blog.png"
width="1600"
height="525"
/>
The more specific image will take precedence over any OG images above it in the folder structure.
> Other image formats such as `jpeg`, `png`, and `gif` are also supported. See the [Open Graph Image docs](/docs/app/api-reference/file-conventions/metadata/opengraph-image) for more information.
## Generated Open Graph images
The [`ImageResponse` constructor](/docs/app/api-reference/functions/image-response) allows you to generate dynamic images using JSX and CSS. This is useful for OG images that depend on data.
For example, to generate a unique OG image for each blog post, add a `opengraph-image.tsx` file inside the `blog` folder, and import the `ImageResponse` constructor from `next/og`:
```tsx filename="app/blog/[slug]/opengraph-image.tsx" switcher
import { ImageResponse } from 'next/og'
import { getPost } from '@/app/lib/data'
// Image metadata
export const size = {
width: 1200,
height: 630,
}
export const contentType = 'image/png'
// Image generation
export default async function Image({ params }: { params: { slug: string } }) {
const post = await getPost(params.slug)
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>
)
)
}
```
```jsx filename="app/blog/[slug]/opengraph-image.js" switcher
import { ImageResponse } from 'next/og'
import { getPost } from '@/app/lib/data'
// Image metadata
export const size = {
width: 1200,
height: 630,
}
export const contentType = 'image/png'
// Image generation
export default async function Image({ params }) {
const post = await getPost(params.slug)
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>
)
)
}
```
`ImageResponse` supports common CSS properties including flexbox and absolute positioning, custom fonts, text wrapping, centering, and nested images. [See the full list of supported CSS properties](/docs/app/api-reference/functions/image-response).
> **Good to know**:
>
> - Examples are available in the [Vercel OG Playground](https://og-playground.vercel.app/).
> - `ImageResponse` uses [`@vercel/og`](https://vercel.com/docs/og-image-generation), [`satori`](https://github.com/vercel/satori), and `resvg` to convert HTML and CSS into PNG.
> - Only flexbox and a subset of CSS properties are supported. Advanced layouts (e.g. `display: grid`) will not work.