Why Next.js SEO Matters
Next.js is one of the most popular React frameworks, but its flexibility means SEO outcomes depend heavily on how you configure rendering, routing, and metadata. A poorly configured Next.js app can be invisible to search engines even when it looks perfect in a browser.
Rendering: Server vs. Client
The most important SEO decision in Next.js is rendering strategy. The App Router supports several rendering modes:
- Server Components (RSC) — Render on the server by default. Content is in the initial HTML, which is ideal for SEO.
- Static Site Generation (SSG) — Pages are pre-rendered at build time. Fast, cacheable, and SEO-friendly.
- Server-Side Rendering (SSR) — Pages render on each request. Necessary for personalized or frequently updated content.
- Client-Side Rendering (CSR) — Content renders in the browser via JavaScript. Search engines may not see the content.
Recommendation
Use Server Components and static generation wherever possible. Reserve client-side rendering for interactive components that do not need to be indexed (dashboards, interactive widgets, filters).
// app/page.tsx — Server Component, SEO-friendly by default
export default async function Page() {
const data = await fetch('https://api.example.com/data');
const json = await data.json();
return <div>{json.title}</div>;
}
Dynamic Metadata
Next.js App Router provides a generateMetadata function for dynamic metadata. Use it for every page:
export async function generateMetadata({ params }) {
const post = await getPost(params.slug);
return {
title: post.title,
description: post.description,
openGraph: { title: post.title, description: post.description },
alternates: { canonical: `${siteUrl}/blog/${post.slug}` },
};
}
Common mistakes:
- Forgetting canonical URLs on parameterized pages
- Using the same title tag across multiple pages
- Not generating OpenGraph images dynamically
- Missing structured data on dynamic pages
Sitemap Generation
Next.js 13+ supports sitemaps via app/sitemap.ts:
export default async function sitemap() {
const posts = await getAllPosts();
const postUrls = posts.map((p) => ({
url: `${siteUrl}/blog/${p.slug}`,
lastModified: p.updatedAt,
priority: 0.7,
}));
return [...staticUrls, ...postUrls];
}
Your sitemap should include every indexable URL. Exclude:
- Noindex pages
- Redirect URLs
- Parameterized URLs that serve the same content
- Admin and authentication pages
Structured Data
Structured data helps search engines understand your content. In Next.js, inject JSON-LD directly into the page:
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
Use schema types that match your content:
Articlefor blog postsProductfor product pagesFAQPagefor FAQ sectionsBreadcrumbListfor navigation trailsOrganizationandWebSitefor site-wide identity
Core Web Vitals
Core Web Vitals measure loading, interactivity, and visual stability. Next.js helps with several built-in optimizations:
- Image component — Automatic lazy loading, responsive srcsets, and format negotiation
- Font optimization — Automatic font preloading and fallback font handling
- Code splitting — Automatic route-based code splitting
- Script component — Control when third-party scripts load
Common Core Web Vitals issues in Next.js:
- Client Components wrapping large subtrees unnecessarily
- Third-party scripts blocking the main thread
- Unoptimized images (use
next/image) - Layout shift from ads or embeds without reserved space
The Bottom Line
Next.js gives you the tools to build SEO-friendly applications, but the defaults are not always optimal. Configure rendering strategy deliberately, generate metadata dynamically, automate sitemaps, add structured data, and monitor Core Web Vitals. SEO is not a plugin you install — it is a set of decisions you make throughout development.