NitroSERP

November 3, 2026·5 min read·By NitroSERP Editorial Team

Updated

Next.js App Router Metadata API, OpenGraph & JSON-LD Guide (2026)

Copy-and-paste Next.js 15 & 16 App Router Metadata API TypeScript examples for title templates, canonical URLs, OpenGraph images, robots rules, and JSON-LD.

The Next.js App Router Metadata API replaces manual <head> tags with strongly-typed TypeScript exports (export const metadata: Metadata for static routes and export async function generateMetadata() for dynamic routes) in app/layout.tsx and app/page.tsx. Properly configured, it guarantees that every route emits a self-referencing canonical URL, OpenGraph and Twitter social cards, googleBot snippet directives, and clean JSON-LD structured data on initial server render.

To generate a complete, copy-pasteable TypeScript configuration for your project in seconds, use our free Next.js Metadata Generator.


Next.js App Router Metadata API Quick Reference Matrix

Metadata FieldWhere to Define ItWhat It Renders in HTML <head>Common Mistake to Avoid
metadataBaseRoot app/layout.tsxResolves relative OpenGraph, Twitter, and canonical paths into absolute https:// URLsOmitting metadataBase, which causes Next.js build warnings and broken relative og:image URLs
title.templateRoot app/layout.tsxAutomatically appends your brand suffix (e.g. %s | Brand) to child route titlesHardcoding the brand suffix inside every child page's title string, resulting in duplicate suffixes (Page | Brand | Brand)
alternates.canonicalEvery indexable page.tsx<link rel="canonical" href="https://..." />Setting alternates.canonical: "/" inside root app/layout.tsx without overriding it in child pages (which points every page's canonical to the homepage)
openGraphlayout.tsx + override in page.tsxog:title, og:description, og:url, og:image, og:typeLeaving openGraph out of child pages—Next.js does a shallow merge, so child pages should explicitly pass their own openGraph.title and openGraph.url
robots.googleBotRoot app/layout.tsx (or noindex pages)<meta name="robots"> and <meta name="googlebot"> directivesForgetting max-image-preview: "large", which is required for full-width Google Discover and mobile SERP thumbnails

1. Root Layout Boilerplate (app/layout.tsx) with metadataBase & Title Templates

Place your global defaults (metadataBase, title.template, and default robots directives) in app/layout.tsx—never put a static alternates.canonical in app/layout.tsx unless every child route overrides it:

import type { Metadata } from "next";

export const metadata: Metadata = {
  metadataBase: new URL("https://www.example.com"),
  title: {
    default: "Acme Dev — Fast Developer & SEO Utilities",
    template: "%s | Acme Dev",
  },
  description:
    "Free browser-based developer and technical SEO utilities with instant TypeScript and JSON-LD output.",
  openGraph: {
    type: "website",
    locale: "en_US",
    siteName: "Acme Dev",
    images: [
      {
        url: "/og-default.png",
        width: 1200,
        height: 630,
        alt: "Acme Dev Tools Preview",
      },
    ],
  },
  twitter: {
    card: "summary_large_image",
    creator: "@acmedev",
  },
  robots: {
    index: true,
    follow: true,
    googleBot: {
      index: true,
      follow: true,
      "max-video-preview": -1,
      "max-image-preview": "large",
      "max-snippet": -1,
    },
  },
};

2. Static Page Metadata Export (app/pricing/page.tsx)

For static pages, export a metadata: Metadata object with a self-referencing alternates.canonical path and route-specific openGraph fields (or build it visually in the Next.js Metadata Generator):

import type { Metadata } from "next";

export const metadata: Metadata = {
  title: "Pricing & Plans",
  description:
    "Compare free and pro plans with transparent monthly pricing and zero hidden usage fees.",
  alternates: {
    canonical: "/pricing",
  },
  openGraph: {
    title: "Pricing & Plans | Acme Dev",
    description:
      "Compare free and pro plans with transparent monthly pricing and zero hidden usage fees.",
    url: "/pricing",
  },
};

Before deploying, paste your rendered title and description into the Google SERP Preview Tool to confirm they stay within desktop pixel and character limits.


3. Dynamic Route generateMetadata() (app/blog/[slug]/page.tsx)

In Next.js 15 and 16, route params is a Promise that must be awaited inside generateMetadata():

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 getPostBySlug(slug);
  if (!post) return {};

  return {
    title: post.title,
    description: post.excerpt,
    alternates: {
      canonical: `/blog/${slug}`,
    },
    openGraph: {
      type: "article",
      title: post.title,
      description: post.excerpt,
      url: `/blog/${slug}`,
      publishedTime: post.publishedAt,
      modifiedTime: post.updatedAt,
      images: [
        {
          url: post.ogImage || `/blog/${slug}/opengraph-image`,
          width: 1200,
          height: 630,
          alt: post.title,
        },
      ],
    },
  };
}

4. Adding XSS-Safe JSON-LD Structured Data in Next.js App Router

Next.js recommends rendering JSON-LD via a standard <script type="application/ld+json"> inside your Server Component while escaping < as \u003c so user content can never break out of the script tag. You can generate validated Article, BreadcrumbList, and FAQPage schemas in our Schema Markup Generator:

export default function BlogPostPage() {
  const jsonLd = {
    "@context": "https://schema.org",
    "@type": "Article",
    headline: "Next.js App Router Metadata API Guide (2026)",
    datePublished: "2026-11-03",
    dateModified: "2026-11-03",
    author: {
      "@type": "Organization",
      name: "NitroSERP Editorial Team",
    },
  };

  return (
    <article>
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{
          __html: JSON.stringify(jsonLd).replace(/</g, "\\u003c"),
        }}
      />
      {/* Page content */}
    </article>
  );
}

5. Complete Next.js Technical SEO Checklist (robots.ts, sitemap.ts & /llms.txt)

  1. Generate export const metadata or generateMetadata(): Use the Next.js Metadata Generator so every indexable page has a unique title, description, and self-referencing canonical URL.
  2. Configure AI & Search Crawler Rules (robots.txt): Allow Googlebot and OAI-SearchBot while blocking training scrapers (GPTBot, CCBot) using the Robots.txt Generator (read our OAI-SearchBot vs. GPTBot guide).
  3. Publish /public/llms.txt: Create a clean Markdown map of your key routes for ChatGPT, Claude, and Perplexity with the free llms.txt Generator.
  4. Verify app/sitemap.ts in Search Console: Ensure your XML sitemap returns Content-Type: application/xml and zero 301/404 URLs—see our guide to fix "Sitemap could not be read" in Google Search Console.