Skip to content
Damien Mercier

SEO with the Next.js App Router: metadata, sitemaps and structured data

Damien Mercier · · 3 min read

Next.jsSEOReact

The Next.js App Router ships everything a technically excellent SEO setup needs (metadata, sitemaps, structured data, streaming server rendering), but the pieces are spread across conventions that are easy to half-use. This is the setup behind the site you are reading, with the reasoning for each piece.

Start with metadataBase and a title template

Every page needs a unique title and description, and every relative URL in your metadata needs a base to resolve against. Both live in the root layout:

// app/layout.tsx
export const metadata: Metadata = {
  metadataBase: new URL("https://example.com"),
  title: {
    default: "Site name | what it does",
    template: "%s | Site name",
  },
  description: "One honest sentence about the site.",
};

The template means child pages only declare their own part:

// app/services/page.tsx
export const metadata: Metadata = {
  title: "Services", // renders as "Services | Site name"
  description: "A unique description for this page.",
  alternates: { canonical: "/services" },
};

Two details that separate a correct setup from a sloppy one:

  • Canonical on every indexable page. With metadataBase set, a relative alternates.canonical is enough, and it protects you when URLs grow query parameters (UTM tags, pagination).
  • Unique descriptions. Duplicated meta descriptions are the most common audit finding on developer sites. If two pages need the same description, one of them probably shouldn't be indexed.

Sitemap and robots as code

The App Router treats these as routes, which means they can't drift out of date if they read from the same source of truth as your pages:

// app/sitemap.ts
export default function sitemap(): MetadataRoute.Sitemap {
  const pages = ["", "/services", "/about", "/contact"];
  const articles = posts.map((post) => `/writing/${post.slug}`);
  return [...pages, ...articles].map((path) => ({
    url: `https://example.com${path}`,
  }));
}

The important habit: generate sitemap entries from the same registry that renders the content. A sitemap maintained by hand is a sitemap that lies.

Structured data without a library

JSON-LD is a <script> tag with a JSON payload; you do not need a package:

<script
  type="application/ld+json"
  dangerouslySetInnerHTML={{ __html: JSON.stringify(articleJsonLd) }}
/>

What matters is discipline, not volume. Emit Person and WebSite once from the layout, Article on articles, BreadcrumbList where breadcrumbs exist, and nothing that doesn't correspond to visible content. Structured data that misrepresents the page is worse for you than none: it is the one part of SEO where over-claiming carries an explicit penalty.

Open Graph images from a route

Instead of exporting static images from a design tool, the opengraph-image file convention generates them at build time with JSX. One typographic template produces a correct, on-brand card for every article, and it can never show a stale title, because it renders from the same data as the page.

Rendering strategy is an SEO decision

The quiet advantage of the App Router: with Server Components and static generation, an SEO-first site ships almost no JavaScript by default. Everything above (metadata, sitemap, JSON-LD) is table stakes; rankings also ride on Core Web Vitals, and no metadata compensates for a slow page. The rules of thumb I follow:

  • Static generation for everything content-shaped; interactivity as small client islands, not client pages.
  • next/image and next/font from day one, because retrofitting layout stability is miserable work.
  • Third-party scripts are guilty until proven necessary.

The performance half of this subject is its own topic, covered in React performance in practice.

The checklist

For every indexable page: unique title, unique description, canonical URL, correct heading hierarchy with one h1, structured data that matches the visible content. Site-wide: sitemap and robots generated from code, Open Graph cards, fast Core Web Vitals.

None of it is glamorous. All of it compounds. Technical SEO is one of the few marketing investments that keeps paying with zero maintenance. This site practises what this article preaches; if you want your product's site or app to do the same, that is work I take on, so let's talk.