Next.jsSEOJSON-LDCore Web Vitals

Mastering Next.js App Router SEO: Metadata, JSON-LD and Core Web Vitals

Everything you need to rank well on Google and be cited by AI systems — from generateMetadata to structured data schemas and Lighthouse optimisation.

Salem Shah··11 min read

Mastering Next.js App Router SEO: Metadata, JSON-LD and Core Web Vitals

Search engines and AI crawlers read your HTML differently than humans do. This guide covers every layer of a production-grade Next.js SEO stack — from the generateMetadata API through JSON-LD structured data to Lighthouse score optimisation.

Why SEO Is Architecture, Not Afterthought

An SEO layer bolted on after launch requires retrofitting semantic HTML, re-writing metadata for hundreds of pages, and hunting down render-blocking resources. When you treat SEO as an architectural concern from day one, every component renders with the right semantics, every page has correct Open Graph tags, and your Lighthouse score stays green through feature additions.

The Next.js Metadata API

Static Metadata

// app/layout.tsx
export const metadata: Metadata = {
  metadataBase: new URL("https://yoursite.com"),
  title: {
    default: "Your Name | Full-Stack Developer",
    template: "%s | Your Name",
  },
  description: "...",
  openGraph: { ... },
  twitter:    { card: "summary_large_image", ... },
};

The template field automatically prefixes page titles — blog posts become "My Article | Your Name" without repeating the site name in every page file.

Dynamic Metadata with generateMetadata

// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }) {
  const { slug } = await params;
  const post = getPostBySlug(slug);
  if (!post) return {};

  return {
    title:       post.title,
    description: post.description,
    openGraph: {
      type:      "article",
      title:     post.title,
      publishedTime: post.date,
      tags:      post.tags,
    },
    alternates: { canonical: `https://yoursite.com/en/blog/${slug}` },
  };
}

Canonical URLs and hreflang

Every localised page must declare its canonical URL and list all language alternates. Google uses these to avoid duplicate-content penalties and to serve the right language to each searcher.

alternates: {
  canonical: `https://yoursite.com/${lang}`,
  languages: { en: ".../en", fr: ".../fr", fa: ".../fa" },
},

JSON-LD Structured Data

Structured data is the primary mechanism by which AI systems — including Google's SGE, ChatGPT Browsing, and Perplexity — extract facts about entities on your page.

Person Schema (for personal portfolios)

{
  "@context": "https://schema.org",
  "@type": "Person",
  "@id": "https://yoursite.com/#person",
  "name": "Your Name",
  "jobTitle": "Full-Stack Developer",
  "knowsAbout": ["React", "Next.js", "Node.js"],
  "sameAs": ["https://github.com/...", "https://linkedin.com/in/..."]
}

The @id anchor makes your identity referenceable across multiple pages. Google's Knowledge Graph merges entities with matching @id values.

TechArticle Schema (for blog posts)

{
  "@context": "https://schema.org",
  "@type": "TechArticle",
  "headline": "...",
  "author": { "@id": "https://yoursite.com/#person" },
  "datePublished": "2026-05-10",
  "keywords": ["Next.js", "SEO", "JSON-LD"]
}

Core Web Vitals Optimisation

LCP (Largest Contentful Paint) — target < 2.5 s

  • Add priority prop to above-the-fold <Image> components
  • Preconnect to external font/image origins: <link rel="preconnect" href="https://fonts.googleapis.com">
  • Serve images in WebP/AVIF via next/image (automatic)

CLS (Cumulative Layout Shift) — target < 0.1

  • Always declare width and height on <Image> — Next.js uses them to reserve space
  • Avoid inserting DOM nodes above existing content after hydration

INP (Interaction to Next Paint) — target < 200 ms

  • Defer non-critical client components with dynamic(() => import(...), { ssr: false })
  • Move heavy computations off the main thread with useTransition

AI Discoverability Checklist

  • [ ] Person schema with knowsAbout array listing specific technologies
  • [ ] sameAs links to GitHub, LinkedIn, professional profiles
  • [ ] FAQPage schema answering the questions AI will try to extract
  • [ ] Semantic heading hierarchy: one <h1> per page, logical <h2>–<h4> nesting
  • [ ] Descriptive <title> and <meta description> — AI uses these for citation snippets
  • [ ] robots.txt allowing all crawlers except private routes
  • [ ] Sitemap with lastModified and hreflang alternates

For a working implementation, see the source code for this portfolio on GitHub.