← Back to Articles

NextJS SEO Playbook for 2026

Pallas Tech Editorial Team

NextJS SEO Playbook for 2026 illustration

The crawl problem hiding in your routes

Plenty of teams find an SEO regression the hard way. Not through a rankings dip, but through a Search Console alert: hundreds of URLs flagged "Discovered, not indexed." The cause usually isn't weak content. It's missing or wrong metadata cascading down from a layout component that no page bothers to override.

The App Router changed how metadata works in NextJS. Dropping tags straight into _document.tsx is gone. What replaced it is a structured, composable system that rewards the teams who learn it and quietly punishes the ones who don't.

This playbook walks through the technical SEO work that moves the needle in a modern NextJS app, ordered by how much each piece affects ranking and indexing.

Metadata hierarchy and the generateMetadata function

The App Router resolves metadata at the segment level. Every layout.tsx and page.tsx can export a static metadata object or an async generateMetadata function. When a layout and a page both export metadata, they merge, and the page wins any conflict.

Static metadata fits pages with fixed titles and descriptions:

export const metadata = {
  title: "How We Build",
  description: "Our engineering practice and delivery approach.",
  openGraph: {
    title: "How We Build",
    description: "Our engineering practice and delivery approach.",
    images: ["/og/how-we-build.png"],
  },
};

Dynamic routes need generateMetadata. It gets the same params and search params the page does, so you can fetch the document and fill in metadata straight from it:

export async function generateMetadata({
  params,
}: {
  params: { slug: string };
}) {
  const post = await getPost(params.slug);
  return {
    title: post.title,
    description: post.excerpt,
    alternates: { canonical: `https://example.com/blog/${params.slug}` },
  };
}

The alternates.canonical field carries more weight than most teams give it. Leave it blank and Google picks a canonical for you, and its pick might not be the URL you wanted indexed.

Sitemap generation that actually keeps up

NextJS supports a sitemap.ts file that runs at build time for static exports and at request time for server-rendered apps. If your site has dynamic routes, the sitemap has to list every path:

import { getAllPosts } from "@/lib/blog";

export default async function sitemap() {
  const posts = await getAllPosts();
  const postUrls = posts.map((p) => ({
    url: `https://example.com/blog/${p.slug}`,
    lastModified: p.date,
    changeFrequency: "monthly" as const,
    priority: 0.7,
  }));

  return [
    { url: "https://example.com", lastModified: new Date(), priority: 1.0 },
    {
      url: "https://example.com/services",
      lastModified: new Date(),
      priority: 0.9,
    },
    ...postUrls,
  ];
}

One rule worth enforcing: set lastModified to when the file or record actually changed, not to today. Stamp today's date on every build and you tell crawlers the whole site churns constantly, which can eat into your crawl budget.

Structured data for rich results

Schema markup is one of the few technical SEO levers that can surface a visible search feature fast. For blog content, Article schema with valid author, datePublished, and dateModified fields is the floor you should hit.

Add it with a JSON-LD component in the page or layout:

export function ArticleSchema({ title, description, date, url }: SchemaProps) {
  const schema = {
    '@context': 'https://schema.org',
    '@type': 'Article',
    headline: title,
    description,
    datePublished: date,
    author: {
      '@type': 'Organization',
      name: 'Your Company',
      url: 'https://example.com',
    },
  }
  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
    />
  )
}

On service pages, Service and LocalBusiness schemas add context that can help with knowledge panel eligibility. On pricing pages, Offer schema puts your prices into product results.

NextJS SEO Playbook for 2026 implementation detail illustration

Robots and indexing control

A robots.ts file in the app directory generates robots.txt at build time. Say plainly what crawlers can and can't reach:

export default function robots() {
  return {
    rules: [{ userAgent: "*", allow: "/" }],
    sitemap: "https://example.com/sitemap.xml",
  };
}

Individual pages can also set their own robots metadata to stay out of the index:

export const metadata = {
  robots: { index: false, follow: false },
};

That beats a global meta robots tag in the root layout, which hits every page and is easy to forget you set.

Core Web Vitals as ranking signals

Once LCP, CLS, and INP became confirmed ranking factors, performance work started paying off directly in SEO. It matters most on the pages pulling the most organic traffic: landing pages, blog posts, service pages.

For those routes:

  • Don't default to client components. Server components render HTML at build or request time, so first paint lands faster.
  • Use next/image with explicit width, height, and priority on above-the-fold images. Skip the dimensions and you get layout shift, which drags CLS down.
  • Keep total JavaScript light on text-heavy pages. A blog post has no business needing 400kB of client JS just to show words.

Run Lighthouse CI in your pipeline with per-route budgets. Catching a regression at PR time costs far less than clawing back rankings after a bad release goes out.

CI enforcement for SEO hygiene

The organizational move with the highest payoff is making SEO quality a gate in the pipeline. Add a metadata check that reads the build output and confirms:

  • Every generated route has a non-empty title and description
  • No two published routes carry the same title
  • Every dynamic route has a canonical URL

Teams that catch this at PR time spend a fraction of the debugging hours that teams relying on a Search Console alert three weeks later end up burning.

Where to focus first

Auditing an existing NextJS app? Work through it in this order:

  1. Check that generateMetadata runs on every dynamic route
  2. Compare sitemap coverage against the real URL count in Search Console
  3. Confirm canonical tags across all routes, especially paginated and filtered views
  4. Validate structured data with Google's Rich Results Test
  5. Run Lighthouse on your top five traffic routes and clear the CLS and LCP issues

Only that last step pulls in design or product. The rest is pure engineering you can ship in a single sprint.

For small and medium-sized businesses

For a smaller team, the payoff here is concrete. You move faster, you carry less operational risk, and a tight budget goes further. Nobody's asking you to adopt every shiny tool. The point is picking the web platform work and the AI-assisted workflows that actually move a number you care about.

Start with one workflow where the economics are obvious. Set a baseline. Improve it in 30-day chunks. Risk stays low, and your team builds real confidence as it goes.

Content & SEO Helpers

As an Amazon Associate I earn from qualifying purchases.