Files
2023-11-16 17:56:02 +00:00

369 lines
13 KiB
Plaintext

---
title: Metadata
description: Use the Metadata API to define metadata in any layout or page.
related:
description: View all the Metadata API options.
links:
- app/api-reference/functions/generate-metadata
- app/api-reference/file-conventions/metadata
- app/api-reference/functions/generate-viewport
---
Next.js has a Metadata API that can be used to define your application metadata (e.g. `meta` and `link` tags inside your HTML `head` element) for improved SEO and web shareability.
There are two ways you can add metadata to your application:
- **Config-based Metadata**: Export a [static `metadata` object](/docs/app/api-reference/functions/generate-metadata#metadata-object) or a dynamic [`generateMetadata` function](/docs/app/api-reference/functions/generate-metadata#generatemetadata-function) in a `layout.js` or `page.js` file.
- **File-based Metadata**: Add static or dynamically generated special files to route segments.
With both these options, Next.js will automatically generate the relevant `<head>` elements for your pages. You can also create dynamic OG images using the [`ImageResponse`](/docs/app/api-reference/functions/image-response) constructor.
## Static Metadata
To define static metadata, export a [`Metadata` object](/docs/app/api-reference/functions/generate-metadata#metadata-object) from a `layout.js` or static `page.js` file.
```tsx filename="layout.tsx | page.tsx" switcher
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: '...',
description: '...',
}
export default function Page() {}
```
```jsx filename="layout.js | page.js" switcher
export const metadata = {
title: '...',
description: '...',
}
export default function Page() {}
```
For all the available options, see the [API Reference](/docs/app/api-reference/functions/generate-metadata).
## Dynamic Metadata
You can use `generateMetadata` function to `fetch` metadata that requires dynamic values.
```tsx filename="app/products/[id]/page.tsx" switcher
import type { Metadata, ResolvingMetadata } from 'next'
type Props = {
params: { id: string }
searchParams: { [key: string]: string | string[] | undefined }
}
export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
// read route params
const id = params.id
// fetch data
const product = await fetch(`https://.../${id}`).then((res) => res.json())
// optionally access and extend (rather than replace) parent metadata
const previousImages = (await parent).openGraph?.images || []
return {
title: product.title,
openGraph: {
images: ['/some-specific-page-image.jpg', ...previousImages],
},
}
}
export default function Page({ params, searchParams }: Props) {}
```
```jsx filename="app/products/[id]/page.js" switcher
export async function generateMetadata({ params, searchParams }, parent) {
// read route params
const id = params.id
// fetch data
const product = await fetch(`https://.../${id}`).then((res) => res.json())
// optionally access and extend (rather than replace) parent metadata
const previousImages = (await parent).openGraph?.images || []
return {
title: product.title,
openGraph: {
images: ['/some-specific-page-image.jpg', ...previousImages],
},
}
}
export default function Page({ params, searchParams }) {}
```
For all the available params, see the [API Reference](/docs/app/api-reference/functions/generate-metadata).
> **Good to know**:
>
> - Both static and dynamic metadata through `generateMetadata` are **only supported in Server Components**.
> - `fetch` requests are automatically [memoized](/docs/app/building-your-application/caching#request-memoization) for the same data across `generateMetadata`, `generateStaticParams`, Layouts, Pages, and Server Components. React [`cache` can be used](/docs/app/building-your-application/caching#request-memoization) if `fetch` is unavailable.
> - Next.js will wait for data fetching inside `generateMetadata` to complete before streaming UI to the client. This guarantees the first part of a [streamed response](/docs/app/building-your-application/routing/loading-ui-and-streaming) includes `<head>` tags.
## File-based metadata
These 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.
For implementation and examples, see the [Metadata Files](/docs/app/api-reference/file-conventions/metadata) API Reference and [Dynamic Image Generation](#dynamic-image-generation).
## Behavior
File-based metadata has the higher priority and will override any config-based metadata.
### 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" />
```
> **Good to know**: You can overwrite the default [`viewport`](/docs/app/api-reference/functions/generate-metadata#viewport) meta tag.
### Ordering
Metadata is evaluated in order, starting from the root segment down to the segment closest to the final `page.js` segment. For example:
1. `app/layout.tsx` (Root Layout)
2. `app/blog/layout.tsx` (Nested Blog Layout)
3. `app/blog/[slug]/page.tsx` (Blog Page)
### Merging
Following the [evaluation order](#ordering), Metadata objects exported from multiple segments in the same route are **shallowly** merged together to form the final metadata output of a route. Duplicate keys are **replaced** based on their ordering.
This means metadata with nested fields such as [`openGraph`](/docs/app/api-reference/functions/generate-metadata#opengraph) and [`robots`](/docs/app/api-reference/functions/generate-metadata#robots) that are defined in an earlier segment are **overwritten** by the last segment to define them.
#### Overwriting fields
```jsx filename="app/layout.js"
export const metadata = {
title: 'Acme',
openGraph: {
title: 'Acme',
description: 'Acme is a...',
},
}
```
```jsx filename="app/blog/page.js"
export const metadata = {
title: 'Blog',
openGraph: {
title: 'Blog',
},
}
// Output:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />
```
In the example above:
- `title` from `app/layout.js` is **replaced** by `title` in `app/blog/page.js`.
- All `openGraph` fields from `app/layout.js` are **replaced** in `app/blog/page.js` because `app/blog/page.js` sets `openGraph` metadata. Note the absence of `openGraph.description`.
If you'd like to share some nested fields between segments while overwriting others, you can pull them out into a separate variable:
```jsx filename="app/shared-metadata.js"
export const openGraphImage = { images: ['http://...'] }
```
```jsx filename="app/page.js"
import { openGraphImage } from './shared-metadata'
export const metadata = {
openGraph: {
...openGraphImage,
title: 'Home',
},
}
```
```jsx filename="app/about/page.js"
import { openGraphImage } from '../shared-metadata'
export const metadata = {
openGraph: {
...openGraphImage,
title: 'About',
},
}
```
In the example above, the OG image is shared between `app/layout.js` and `app/about/page.js` while the titles are different.
#### Inheriting fields
```jsx filename="app/layout.js"
export const metadata = {
title: 'Acme',
openGraph: {
title: 'Acme',
description: 'Acme is a...',
},
}
```
```jsx filename="app/about/page.js"
export const metadata = {
title: 'About',
}
// Output:
// <title>About</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acme is a..." />
```
**Notes**
- `title` from `app/layout.js` is **replaced** by `title` in `app/about/page.js`.
- All `openGraph` fields from `app/layout.js` are **inherited** in `app/about/page.js` because `app/about/page.js` doesn't set `openGraph` metadata.
## Dynamic Image Generation
The `ImageResponse` constructor allows you to generate dynamic images using JSX and CSS. This is useful for creating social media images such as Open Graph images, Twitter cards, and more.
`ImageResponse` uses the [Edge Runtime](/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes#edge-runtime), and Next.js automatically adds the correct headers to cached images at the edge, helping improve performance and reducing recomputation.
To use it, you can import `ImageResponse` from `next/og`:
```jsx filename="app/about/route.js"
import { ImageResponse } from 'next/og'
export const runtime = 'edge'
export async function GET() {
return new ImageResponse(
(
<div
style={{
fontSize: 128,
background: 'white',
width: '100%',
height: '100%',
display: 'flex',
textAlign: 'center',
alignItems: 'center',
justifyContent: 'center',
}}
>
Hello world!
</div>
),
{
width: 1200,
height: 600,
}
)
}
```
`ImageResponse` integrates well with other Next.js APIs, including [Route Handlers](/docs/app/building-your-application/routing/route-handlers) and file-based Metadata. For example, you can use `ImageResponse` in a `opengraph-image.tsx` file to generate Open Graph images at build time or dynamically at request time.
`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/concepts/functions/edge-functions/og-image-generation), [Satori](https://github.com/vercel/satori), and Resvg to convert HTML and CSS into PNG.
> - Only the Edge Runtime is supported. The default Node.js runtime will not work.
> - Only flexbox and a subset of CSS properties are supported. Advanced layouts (e.g. `display: grid`) will not work.
> - Maximum bundle size of `500KB`. The bundle size includes your JSX, CSS, fonts, images, and any other assets. If you exceed the limit, consider reducing the size of any assets or fetching at runtime.
> - Only `ttf`, `otf`, and `woff` font formats are supported. To maximize the font parsing speed, `ttf` or `otf` are preferred over `woff`.
## JSON-LD
[JSON-LD](https://json-ld.org/) is a format for structured data that can be used by search engines to understand your content. For example, you can use it to describe a person, an event, an organization, a movie, a book, a recipe, and many other types of entities.
Our current recommendation for JSON-LD is to render structured data as a `<script>` tag in your `layout.js` or `page.js` components. For example:
```tsx filename="app/products/[id]/page.tsx" switcher
export default async function Page({ params }) {
const product = await getProduct(params.id)
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.name,
image: product.image,
description: product.description,
}
return (
<section>
{/* Add JSON-LD to your page */}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
{/* ... */}
</section>
)
}
```
```jsx filename="app/products/[id]/page.js" switcher
export default async function Page({ params }) {
const product = await getProduct(params.id)
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.name,
image: product.image,
description: product.description,
}
return (
<section>
{/* Add JSON-LD to your page */}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
{/* ... */}
</section>
)
}
```
You can validate and test your structured data with the [Rich Results Test](https://search.google.com/test/rich-results) for Google or the generic [Schema Markup Validator](https://validator.schema.org/).
You can type your JSON-LD with TypeScript using community packages like [`schema-dts`](https://www.npmjs.com/package/schema-dts):
```tsx
import { Product, WithContext } from 'schema-dts'
const jsonLd: WithContext<Product> = {
'@context': 'https://schema.org',
'@type': 'Product',
name: 'Next.js Sticker',
image: 'https://nextjs.org/imgs/sticker.png',
description: 'Dynamic at the speed of static.',
}
```