SEO & Metadata

Note: Built-in support for SEO through metadata, introduced in 13.2, replaces the previous head.js implementation. The head.js special file was deprecated in 13.2 and will be removed in a future version. View migration guide.

Next.js allows you to define metadata (e.g. meta and link tags inside your HTML head element) with an explicit metadata configuration in any layout or page.


Both static and dynamic metadata through generateMetadata are only supported in Server Components.

Static Metadata

import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'Home',
  description: 'Welcome to Next.js',

export default function Page() {
  return '...'

Dynamic Metadata

You can use generateMetadata to fetch metadata that requires dynamic values.

import type { Metadata } from 'next'

// The `fetch` response is cached and reused between both functions
// below, resulting in a single API request. If you cannot use `fetch`
// directly, you can use `cache`. Learn more:
async function getProduct(id) {
  const res = await fetch(`https://.../api/products/${id}`);
  return res.json();

export async function generateMetadata({ params }): Promise<Metadata> {
  const product = await getProduct(;
  return { title: product.title }

export default async function Page({ params }) {
  const product = await getProduct(;
  // ...

Good to know:

  • When rendering a route, Next.js will automatically dedupe requests for the same data across generateStaticParams, generateMetadata, Layouts, Pages, and Server Components.
  • 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 includes <head> tags.


JSON-LD 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:

export default async function Page({ params }) {
  const product = await getProduct(;

  const jsonLd = {
    '@context': '',
    '@type': 'Product',
    image: product.image,
    description: product.description,

  return (
      {/* Add JSON-LD to your page */}
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      {/* ... */}

You can validate and test your structured data with the Rich Results Test for Google or the generic Schema Markup Validator.

You can type your JSON-LD with TypeScript using community packages like schema-dts:

import { Product, WithContext } from 'schema-dts';

const jsonLd: WithContext<Product> = {
  '@context': '',
  '@type': 'Product',
  name: 'Next.js Sticker',
  image: '',
  description: 'Dynamic at the speed of static.',

Next Steps