SEO for Next.js 2026 — Rank on Google, Appear in AI Search, and Google Discover
Advertisement
Introduction
Why This Matters
SEO in 2026 means ranking on Google AND appearing in AI answer engines (ChatGPT, Perplexity, Gemini). AI engines pull from the same well as Google — structured data, clear headings, and factual content with citations. Next.js App Router has first-class metadata support that makes technical SEO straightforward.
Metadata API
// app/layout.tsx — site-wide defaults
import type { Metadata } from 'next'
export const metadata: Metadata = {
metadataBase: new URL('https://yoursite.com'),
title: {
default: 'Your Site Name',
template: '%s | Your Site Name',
},
description: 'The best resource for modern web development.',
openGraph: {
siteName: 'Your Site Name',
locale: 'en_US',
type: 'website',
},
twitter: {
card: 'summary_large_image',
site: '@yourtwitterhandle',
},
robots: {
index: true,
follow: true,
googleBot: { index: true, follow: true, 'max-image-preview': 'large' },
},
}// app/blog/[slug]/page.tsx — dynamic metadata per page
import type { Metadata } from 'next'
import { getPost } from '@/lib/posts'
import { notFound } from 'next/navigation'
export async function generateMetadata(
{ params }: { params: Promise<{ slug: string }> }
): Promise<Metadata> {
const { slug } = await params
const post = await getPost(slug)
if (!post) return {}
return {
title: post.title,
description: post.excerpt,
authors: [{ name: post.author.name }],
publishedTime: post.publishedAt,
openGraph: {
title: post.title,
description: post.excerpt,
type: 'article',
publishedTime: post.publishedAt,
authors: [post.author.name],
images: [{
url: `/og?title=${encodeURIComponent(post.title)}`,
width: 1200,
height: 630,
alt: post.title,
}],
},
twitter: {
card: 'summary_large_image',
title: post.title,
description: post.excerpt,
},
}
}Dynamic OG Images
// app/og/route.tsx
import { ImageResponse } from 'next/og'
export async function GET(request: Request) {
const { searchParams } = new URL(request.url)
const title = searchParams.get('title') ?? 'Untitled'
return new ImageResponse(
(
<div
style={{
display: 'flex',
flexDirection: 'column',
width: '100%',
height: '100%',
padding: '64px',
background: 'linear-gradient(135deg, #1e293b 0%, #0f172a 100%)',
color: 'white',
fontFamily: 'sans-serif',
}}
>
<div style={{ fontSize: 16, color: '#94a3b8', marginBottom: 24 }}>
yoursite.com
</div>
<div style={{ fontSize: 56, fontWeight: 700, lineHeight: 1.2, flex: 1 }}>
{title}
</div>
</div>
),
{ width: 1200, height: 630 }
)
}Structured Data (JSON-LD)
Structured data helps Google and AI engines understand your content:
// components/ArticleSchema.tsx
export function ArticleSchema({ post }: { post: Post }) {
const schema = {
'@context': 'https://schema.org',
'@type': 'Article',
headline: post.title,
description: post.excerpt,
author: {
'@type': 'Person',
name: post.author.name,
url: `https://yoursite.com/authors/${post.author.slug}`,
},
publisher: {
'@type': 'Organization',
name: 'Your Site Name',
logo: { '@type': 'ImageObject', url: 'https://yoursite.com/logo.png' },
},
datePublished: post.publishedAt,
dateModified: post.updatedAt,
image: post.coverImage,
url: `https://yoursite.com/blog/${post.slug}`,
}
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
)
}// FAQ structured data for AI engines
export function FAQSchema({ faqs }: { faqs: Array<{ q: string; a: string }> }) {
const schema = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: faqs.map(({ q, a }) => ({
'@type': 'Question',
name: q,
acceptedAnswer: { '@type': 'Answer', text: a },
})),
}
return (
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
/>
)
}Sitemap and robots.txt
// app/sitemap.ts
import type { MetadataRoute } from 'next'
import { getAllPosts } from '@/lib/posts'
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const posts = await getAllPosts()
const postUrls = posts.map(post => ({
url: `https://yoursite.com/blog/${post.slug}`,
lastModified: new Date(post.updatedAt),
changeFrequency: 'weekly' as const,
priority: 0.8,
}))
return [
{ url: 'https://yoursite.com', lastModified: new Date(), priority: 1 },
{ url: 'https://yoursite.com/blog', lastModified: new Date(), priority: 0.9 },
...postUrls,
]
}// app/robots.ts
import type { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
return {
rules: [
{ userAgent: '*', allow: '/' },
{ userAgent: 'Googlebot', allow: '/' },
],
sitemap: 'https://yoursite.com/sitemap.xml',
}
}Common Mistakes
- Setting the same
titleanddescriptionon every page — duplicate metadata hurts ranking - Not using
metadataBasein Next.js — relative image URLs in OG tags do not resolve correctly - Missing
datePublishedanddateModifiedin Article schema — Google uses these for freshness signals - Not generating a sitemap — search engines may miss new pages for weeks
- Ignoring Core Web Vitals — LCP, CLS, and INP are confirmed ranking signals
Best Practices
- Write
descriptiontags as a complete sentence that answers what the page covers - Use
template: '%s | Site Name'in the root layout so page titles stay consistent - Include FAQ structured data on content pages — AI engines prefer clear question-answer pairs
- Generate OG images dynamically with
ImageResponseso every page has a unique social card - Submit your sitemap in Google Search Console and monitor index coverage weekly
Key Takeaways
- Next.js
generateMetadatasupports async data — use it for dynamic titles and descriptions per page metadataBaseis required for Next.js to resolve relative image URLs in OG and Twitter card tags- JSON-LD structured data helps both Google and AI answer engines understand your content type
- FAQ and HowTo schemas increase the chance of appearing in AI-generated answer summaries
app/sitemap.tsexports a typed sitemap automatically served at/sitemap.xml- Core Web Vitals (LCP, CLS, INP) are ranking factors — performance optimization is SEO optimization
- Dynamic OG images with
ImageResponserun on the Edge and add zero build time - Canonical URLs prevent duplicate content penalties when content appears at multiple paths
Advertisement
Related reading
Next.js Metadata API — SEO, Open Graph, and Dynamic Meta Tags6 min readGoogle Gemini API Guide 2026 — Build AI Apps with Gemini 2.0 Flash5 min readBuild an AI Chatbot with Next.js 15 and OpenAI — Full Stack 20266 min readGoogle Gemini Complete Guide — Models, Features, and API 20266 min readPerplexity AI — The AI Search Engine for Developers 20266 min read