Skip to main content
Next.js learning series · Blog 11

Next.js 16 Metadata and SEO Explained

Create accurate search metadata, canonical URLs, social previews, crawler files, and structured data with current App Router APIs.

You now know how to build pages, data flows, mutations, and HTTP endpoints with Route Handlers. The next step is making sure every important page has accurate metadata for search engines, browsers, and social sharing.

Metadata is not a ranking trick. It describes a page and helps platforms present it, while sustainable SEO also depends on useful content, crawlability, crawlable internal links, performance, accessibility, search intent, and site reputation.

Metadata at a Glance

The Metadata API turns typed configuration into document head tags. Static pages can export metadata; data-driven pages can export generateMetadata; special files can define icons, manifests, social images, robots rules, and sitemaps.

NeedApp Router tool
Known title and descriptionStatic metadata
Post-specific valuesgenerateMetadata
Canonical and absolute social URLsmetadataBase + URL metadata fields
Icons and social imagesMetadata fields or file conventions
Robots and sitemap endpointsrobots.ts and sitemap.ts
Structured dataSafely serialized JSON-LD script
Project version check

The downloadable starter declares "next": "latest" without a lockfile or installed dependency tree. No exact patch version can be verified. These examples follow the current official Next.js 16 documentation; production projects should pin a version and commit the lockfile.

Diagram 1: Metadata connects a page to search and socialOne page provides title and description to search, image and summary to social previews, and a title to browser tabs.
Next.js page
Search
Title · description · canonical
Social
Image · title · summary
Browser
Tab title · icon

Static Metadata with metadata

Export a typed object from a layout.tsx or page.tsx Server Component when values do not depend on request or content data. Metadata exports are not supported in Client Components. Keep the page server-side and move interactive UI into a child Client Component.

app/about/page.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: 'About NavTech Solution',
  description: 'Learn about NavTech Solution and our development work.',
}

export default function AboutPage() {
  return <main>...</main>
}

Layouts, Pages, and Metadata Merging

Metadata is evaluated from the root layout through nested layouts to the final page. Fields are shallowly merged, and later duplicate keys replace earlier values. This matters for nested objects: defining openGraph in a child replaces the parent openGraph object instead of deeply merging missing properties. Share reusable fragments explicitly when needed. Review layouts and pages for the underlying segment hierarchy.

Diagram 2: Metadata evaluation and shallow mergingRoot metadata is evaluated first, blog layout metadata second, and page metadata last; duplicate top-level fields are replaced by the closest segment.
  1. Root layout
    site defaults
  2. Blog layout
    section fields
  3. Post page
    specific fields
  4. Final metadata
    shallow merge

Title Templates

app/layout.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: {
    default: 'NavTech Solution',
    template: '%s | NavTech Solution',
  },
}

A template needs a default and applies to titles in child route segments, not the page at the same segment where the template is declared. A child title of Next.js Metadata Guide becomes Next.js Metadata Guide | NavTech Solution. Use title.absolute when a route must intentionally ignore a parent template.

Diagram 3: How title templates workA root template inserts the child page title at the percent-s placeholder to produce the final document title.
%s | NavTech Solution+Next.js SEONext.js SEO | NavTech Solution

Writing Useful Meta Descriptions

Give important indexable pages a concise, specific description matching visible content and search intent. Avoid repeated boilerplate, keyword lists, fabricated benefits, and claims the article cannot support. A supplied description is a suggestion: search engines may generate a different query-specific snippet.

Dynamic Metadata with generateMetadata

In Next.js 16, dynamic route params are Promises. Await the slug, load the same trusted post record used by the page, and return typed metadata. You cannot export both static metadata and generateMetadata from the same segment.

app/blog/[slug]/page.tsx
import type { Metadata } from 'next'
import { notFound } from 'next/navigation'

type Props = { params: Promise<{ slug: string }> }

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  const { slug } = await params
  const post = await getPost(slug)
  if (!post) notFound()

  return {
    title: post.title,
    description: post.description,
    alternates: { canonical: `/blog/${post.slug}` },
  }
}

Identical fetch requests are automatically memoized across metadata, layouts, pages, and Server Components; use React cache when the data source is not fetch. Missing content can call notFound(), connecting metadata resolution with the safe 404 patterns from Blog #9.

Dynamic metadata may stream after initial UI for capable clients. Next.js blocks rendering for HTML-limited bots so metadata remains in the head they inspect. The default bot handling is appropriate for most sites; changing htmlLimitedBots can increase response time.

A dynamic blog slug enters generateMetadata and produces title, canonical, Open Graph, and robots metadata
Content drives dynamic metadata. The route slug resolves one trusted post record before metadata fields are produced.
Diagram 4: Dynamic blog metadata flowA URL slug reaches generateMetadata, loads a post, and returns title, description, canonical, and social metadata.
  1. /blog/slug
  2. await params
  3. generateMetadata
  4. load post
  5. render metadata

Canonical URLs and metadataBase

A canonical identifies the preferred URL for duplicate or substantially equivalent content. It is not a redirect and does not guarantee a search engine will select it. Configure the known production origin rather than reading an untrusted Host header or accidentally canonicalizing a preview domain.

Trusted root metadata
export const metadata: Metadata = {
  metadataBase: new URL('https://navtechsolution.com'),
  alternates: { canonical: '/' },
}

metadataBase resolves relative URL-based metadata fields. An absolute field ignores it; a relative URL without a configured base can cause a build error. Keep the production site URL in validated server configuration when environments differ.

Diagram 5: metadataBase resolves a relative canonicalThe trusted production origin and relative blog path combine into one absolute canonical URL.
https://navtechsolution.com+/blog/nextjs-metadataAbsolute canonical

Open Graph and Twitter/X Metadata

Open Graph fields shape link previews on many services. Twitter/X metadata provides platform-oriented card fields, although platforms can change their rendering and may fall back to Open Graph. Reference only real images with accurate dimensions and useful alt text.

Page-specific social metadata
export const metadata: Metadata = {
  openGraph: {
    title: 'Next.js Metadata Guide',
    description: 'Build accurate App Router metadata.',
    url: '/blog/nextjs-metadata',
    type: 'article',
    images: [{
      url: '/assets/images/blog/nextjs-metadata-seo/nextjs-16-metadata-seo.webp',
      width: 1672,
      height: 941,
      alt: 'Next.js Metadata Guide',
    }],
  },
  twitter: {
    card: 'summary_large_image',
    title: 'Next.js Metadata Guide',
    description: 'Build accurate App Router metadata.',
    images: ['/assets/images/blog/nextjs-metadata-seo/nextjs-16-metadata-seo.webp'],
  },
}

The example uses Blog #11's verified production asset rather than a placeholder URL. In a Next.js application, file-based opengraph-image and twitter-image conventions can use static JPG, PNG, or GIF files, or generated TSX routes. More specific segment images take precedence.

Page title, description, image, and canonical values flowing into search and social preview cards
A preview is a content contract. Titles, descriptions, URLs, and images should all describe the same visible page.

Robots Directives and robots.txt

Page-level robots metadata
export const metadata: Metadata = {
  robots: { index: true, follow: true },
}

index permits indexing; noindex asks compliant crawlers not to index. follow and nofollow influence link-following behavior. Apply noindex intentionally to previews, internal search results, or private environments, not globally to production.

app/robots.ts
import type { MetadataRoute } from 'next'

export default function robots(): MetadataRoute.Robots {
  return {
    rules: { userAgent: '*', allow: '/', disallow: '/private/' },
    sitemap: 'https://navtechsolution.com/sitemap.xml',
  }
}

A static app/robots.txt is also valid. Robots rules express crawler preferences; they do not authenticate users or protect secrets. Sensitive routes need real authorization and should not expose private data even when directly requested.

Creating a Sitemap

app/sitemap.ts
import type { MetadataRoute } from 'next'

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const posts = await getPublishedPosts()
  return posts.map((post) => ({
    url: `https://navtechsolution.com/blog/${post.slug}`,
    lastModified: post.updatedAt,
  }))
}

Use real published records and actual modification dates. Do not stamp every URL with the build time. Exclude private, admin, preview, and parameter-noise URLs. A sitemap improves discovery; submission does not guarantee crawling, indexing, or ranking.

Diagram 7: Sitemap discovery flowA sitemap lists public canonical pages for crawler discovery without guaranteeing indexing.
Published website
//blog/blog/post-1/blog/post-2
Search crawlers

File-Based Metadata Conventions

ConventionPurpose
favicon.icoRoot browser icon
icon.*, apple-icon.*App and device icons
opengraph-image.*Open Graph image
twitter-image.*Twitter/X image
manifest.json or manifest.tsWeb app manifest
robots.txt or robots.tsCrawler rules
sitemap.xml or sitemap.tsPublic URL discovery

File-based metadata has higher priority than configuration returned by metadata or generateMetadata. Avoid accidentally maintaining two competing sources for the same field.

Structured Data with JSON-LD

JSON-LD is separate from the Metadata object. It should describe real visible entities using real authors, dates, URLs, and images. Never add fabricated ratings, reviews, credentials, or freshness dates.

Safe Article JSON-LD serialization
const jsonLd = {
  '@context': 'https://schema.org',
  '@type': 'Article',
  headline: post.title,
  description: post.description,
  datePublished: post.publishedAt,
  dateModified: post.updatedAt,
  url: `https://navtechsolution.com/blog/${post.slug}`,
}

<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{
    __html: JSON.stringify(jsonLd).replace(/</g, '\\u003c'),
  }}
/>

Escaping less-than characters reduces script-breakout risk when values can contain user-controlled text. Follow the organization's sanitization policy. Visible breadcrumbs and BreadcrumbList should name and order the same Home → Blog → Next.js → article path.

Pagination, Duplicate Content, and Canonicals

Do not automatically canonicalize every paginated page to page one. Page two may expose distinct items and deserve its own self-referencing canonical. Decide how filters, sorting, tracking parameters, print views, syndication, and locale variants should be indexed before generating metadata. Canonicals should represent real equivalence, not hide architecture problems.

Build SEO for a Dynamic Blog Post

A practical post pipeline starts from one trusted content record containing title, description, slug, publication and modification dates, image, and indexability. The same record should drive generateMetadata, visible H1 and summary, Article JSON-LD, breadcrumbs, social image, and sitemap entry. This prevents contradictory titles, broken images, and fake freshness.

Content, metadata, canonical, JSON-LD, internal links, sitemap, and performance working together for search and social discovery
SEO is a coordinated system. Accurate metadata supports good content and architecture; it cannot replace them.
Diagram 8: Complete blog SEO pipelineOne content record drives metadata, the visible page, structured data, breadcrumbs, internal links, and sitemap discovery.
  1. Post content
  2. generateMetadata
  3. Canonical + social
  4. Page + JSON-LD
  5. Links + sitemap
  6. Search + social
Diagram 9: Page SEO layersVisible content, metadata, canonical, structured data, internal links, sitemap discovery, performance, and accessibility work together.
Useful page
Visible contentMetadataCanonicalJSON-LDInternal linksSitemapPerformance + accessibility

Common Next.js Metadata and SEO Mistakes

  • Using the same title or description on every page.
  • Exporting metadata from a Client Component.
  • Pointing canonicals to previews, staging, or the wrong protocol.
  • Publishing nonexistent OG image URLs.
  • Forgetting dynamic metadata for blog posts.
  • Assuming nested metadata objects merge deeply.
  • Keyword stuffing titles and descriptions.
  • Adding fake lastModified values to a sitemap.
  • Using robots.txt as authentication.
  • Adding JSON-LD that does not match visible content.
  • Fabricating ratings, reviews, authors, or dates.
  • Canonicalizing all pagination to page one without analysis.
  • Assuming sitemap submission guarantees indexing.
  • Expecting metadata to compensate for weak content.

Next.js Metadata and SEO Best Practices

  • Use static metadata for known values and dynamic metadata only when data requires it.
  • Configure one trusted production site origin.
  • Create unique, accurate titles and descriptions for important pages.
  • Keep canonical, Open Graph, JSON-LD, and visible content consistent.
  • Reuse real content records and shared metadata fragments.
  • Verify every referenced image exists and has accurate dimensions.
  • Keep private and preview URLs out of the public sitemap.
  • Use real publication and modification dates.
  • Test generated production HTML, sitemap, robots file, and share images.
  • Maintain crawlable internal navigation and useful page content.

Inspecting Metadata

Inspect the production page source and rendered DOM, then verify the title, description, canonical, robots directives, social image URL, and JSON-LD script. Open the generated sitemap and robots.txt directly. Validate structured data with appropriate validators, but remember that passing a tool does not guarantee search visibility or ranking.

FAQ

What is the Metadata API in Next.js 16?

It is the App Router system for declaring titles, descriptions, canonicals, social metadata, robots directives, icons, and related head information through typed server-side APIs and file conventions.

How do I set a page title in the App Router?

Export a typed metadata object from a Server Component page or layout when the title is static, or return it from generateMetadata when it depends on route data.

What is generateMetadata?

generateMetadata is a server-only function that resolves metadata from route params, search params, parent metadata, or fetched content as part of rendering a page.

How do I set a canonical URL in Next.js?

Set alternates.canonical in metadata. Configure a trusted production metadataBase when using relative canonical values.

What does metadataBase do?

metadataBase resolves relative URL-based metadata fields, such as canonical and Open Graph image URLs, against one configured base URL.

How do I add Open Graph metadata?

Use the openGraph metadata field or a real opengraph-image file convention. Include an accurate title, description, URL, type, and existing image.

How do I create a sitemap in Next.js?

Add app/sitemap.ts returning MetadataRoute.Sitemap, or use a static app/sitemap.xml. Populate URLs and modification dates from real content.

How do I create robots.txt in Next.js?

Add a static app/robots.txt or return MetadataRoute.Robots from app/robots.ts. Robots rules are crawler preferences, not access control.

Can I generate metadata dynamically for blog posts?

Yes. Await the Promise-based slug params in generateMetadata, load the post, handle missing content, and return metadata built from its real fields.

How do I add JSON-LD in Next.js?

Render a script with type application/ld+json in the page or layout and safely serialize real structured data, escaping less-than characters to reduce injection risk.

Does metadata improve Google rankings?

Metadata helps systems understand and present pages, but it does not guarantee rankings. Content quality, crawlability, links, performance, accessibility, intent, and reputation also matter.

Should every page have a unique meta description?

Important indexable pages should normally have an accurate, useful description. Search engines may still generate a different snippet for a specific query.

Official Resources

Next Steps

You can now build a trustworthy metadata system for static pages, dynamic posts, social previews, crawler discovery, and structured data. Blog #12 will cover Next.js 16 Image Optimization; it is not published yet, so no broken link is added.

WhatsApp