export const metadata

Declare a segment's static title, description and social tags as a plain exported object.

Signature
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: string | { default: string; template?: string; absolute?: string },
  description: string,
  metadataBase: URL,
  openGraph: OpenGraph,
  robots: Robots,
  alternates: AlternateURLs,
  icons: Icons,
  // ...
}
  • Added in Next.js v13.2.0

Parameters

ParameterTypeDescription
$titleoptionalstring | { default: string; template?: string; absolute?: string }The document title. default supplies a fallback for children, template wraps a child's title via %s, and absolute ignores any parent template.
$descriptionoptionalstringBecomes <meta name="description">.
$metadataBaseoptionalURLBase URL that relative values in URL-based fields are resolved against, for this segment and below. Normally set once in the root layout.
$openGraphoptionalOpenGraphog:* tags — title, description, url, siteName, images, videos, type and more. Replaced wholesale, not merged, by any descendant that sets it.
$robotsoptionalstring | Robotsindex, follow, nocache and a nested googleBot object with max-snippet, max-image-preview and max-video-preview.
$alternatesoptionalAlternateURLscanonical, plus languages, media and types maps that emit <link rel="alternate"> tags.
$iconsoptionalstring | Iconsicon, shortcut, apple and other entries. File-based conventions such as icon.tsx are preferred and override this.

Return value

Not a function — a named export whose fields Next.js converts into <head> tags. Objects exported from several segments of the same route are shallowly merged, root first, with later segments replacing duplicate keys.

Errors

A relative URL in a URL-based field with no metadataBase configured is a build error, as is exporting both metadata and generateMetadata from one segment.

Changelog

  • PHP v14.0.0viewport, themeColor and colorScheme were deprecated as metadata fields; they now belong to the separate viewport export.
  • PHP v13.2.0Introduced alongside generateMetadata.

This is the default choice. If a route's tags are literals — a marketing page, an about page, the site-wide defaults in the root layout — export the object and Next.js resolves it at build time with nothing to await. Only when a value genuinely depends on params, a query string or fetched data should you switch to generateMetadata; a segment may export one or the other, never both.

Where to put what

The root layout owns metadataBase, title.template, title.default and the fallback openGraph block. Pages then override the individual keys they care about. Without metadataBase, a relative openGraph.images or alternates.canonical is a build error rather than a silent miss.

What trips people up

  • Shallow merge. Setting one openGraph field in a page discards the layout's entire openGraph object.
  • title.template applies to children, so it never affects the title in the same file, and it does nothing in a page.tsx.
  • Server Components only — the export is inert in a 'use client' file.
  • File conventions such as opengraph-image.tsx outrank whatever you write here.

Example

Example · typescript
import type { Metadata } from 'next'

export const metadata: Metadata = {
  metadataBase: new URL('https://acme.com'),
  title: {
    template: '%s | Acme',
    default: 'Acme — tools for small teams',
  },
  description: 'Acme builds scheduling tools for teams of five to fifty.',
  openGraph: {
    type: 'website',
    siteName: 'Acme',
    images: ['/og/default.png'], // resolved against metadataBase
  },
  alternates: {
    canonical: '/',
    languages: {
      'en-US': '/en-US',
      'de-DE': '/de-DE',
    },
  },
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

When to use it

  • Set the site-wide title template, description and OG defaults in the root layout.
  • Give a static marketing page its own title and share image.
  • Point a duplicated route at its canonical URL.
  • Keep a staging or internal route out of search results with robots.index false.

Frequently asked questions

Why is my metadata export being ignored?

The three usual causes are a 'use client' directive in the file (the export is only supported in Server Components), a same-file generateMetadata export (a segment may declare one or the other), and a file-based convention such as opengraph-image.tsx or icon.tsx, which takes priority over the equivalent field. Also check that it is a top-level export named exactly metadata.

What is metadataBase for in Next.js?

It sets the base URL that relative values in URL-based fields — openGraph.images, alternates.canonical, twitter.images — are resolved against, for the segment that declares it and everything below. Set it once in the root layout; without it, a relative path in one of those fields fails the build, and an absolute value in a field ignores it.

Should I use the metadata object or generateMetadata?

Use the static object whenever the values do not depend on request information — it resolves at build time and never blocks or streams. Switch to generateMetadata only for tags derived from params, searchParams or fetched data.

More examples

A page overriding the layout, and the merge result

Two routes, two different outcomes from the same rule. /about inherits the layout's whole openGraph block because it declares none of its own. /blog declares openGraph, so the layout's version is replaced key for key — og:description vanishes even though the page never mentioned it. This shallow replacement is the single most reported surprise in the Metadata API.

Example · typescript
// app/layout.tsx
export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
}

// app/about/page.tsx — sets no openGraph
export const metadata = {
  title: 'About',
}

// Output for /about:
// <title>About</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acme is a..." />

// app/blog/page.tsx — sets one openGraph field
export const metadata = {
  title: 'Blog',
  openGraph: {
    title: 'Blog',
  },
}

// Output for /blog:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />
// og:description is GONE

Keeping a route out of search results

robots is expanded into the standard directive strings, with the nested googleBot object emitted as its own tag. title.absolute stops the root layout's '%s | Acme' template from appending the public brand name to an internal page. A robots meta tag only asks crawlers not to index — it does not restrict access, so pair it with real authorisation.

Example · typescript
// app/internal/reports/page.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: { absolute: 'Internal reports' },
  robots: {
    index: false,
    follow: false,
    nocache: true,
    googleBot: {
      index: false,
      follow: false,
      noimageindex: true,
    },
  },
}

export default function Page() {
  return <h1>Internal reports</h1>
}

// <head> output:
// <title>Internal reports</title>
// <meta name="robots" content="noindex, nofollow, nocache" />
// <meta name="googlebot" content="noindex, nofollow, noimageindex" />

Discussion

  • Be the first to comment on this lesson.