React applications have a reputation for being invisible to search engines. That reputation is outdated, but the underlying concern is valid: if your content is rendered entirely in the browser with JavaScript, Google has to execute that JavaScript to see it. That process is slower, less reliable, and lower priority than reading server-rendered HTML.
Next.js solves this. It supports server-side rendering (SSR), static site generation (SSG), and incremental static regeneration (ISR) out of the box. But having the right rendering strategy available does not mean developers use it correctly. Most Next.js SEO problems come from misconfiguration, not platform limitations.
Here is the technical checklist.
Metadata: the generateMetadata API
In the App Router, metadata is handled through the generateMetadata function or a static metadata export in your page or layout files. This replaces the old <Head> component from the Pages Router.
A ChatGPT SEO audit is a manual site review where you feed page data, crawl output, or Google Search Console exports into ChatGPT and use targeted prompts to identify technical issues, content gaps, and ranking opportunities. It works best for analysis and pri
The "duplicate content penalty" is one of the most persistent myths in SEO. Site owners panic when they find identical text on two URLs, convinced that Google is about to punish their entire domain. That is not how it works.
Google does not have a penalty for
Google Images is one of the largest search engines in the world. For industries where visuals drive decisions, like e-commerce, travel, interior design, food, and fashion, image search represents a real traffic channel that most sites completely ignore.
The ba
},
}
}
Every page that should appear in search results needs a unique title and description. This seems obvious, but it is the most common gap on Next.js sites. Developers build layouts with a single metadata export that applies site-wide, then never override it on individual pages. The result: every page has the same title tag.
For static pages, use the metadata export directly. For dynamic pages (blog posts, product pages, profile pages), use generateMetadata to pull the data and construct unique metadata per page. Always include openGraph properties for social sharing.
Sitemaps and robots.txt
Next.js App Router supports both programmatically.
For larger sites with thousands of URLs, use the generateSitemaps function to create multiple sitemap files with a sitemap index. Google recommends keeping individual sitemaps under 50,000 URLs.
The next-sitemap package is an alternative that generates sitemaps at build time and offers more configuration options (priority, changefreq, exclusion patterns). Either approach works. The built-in sitemap.ts is simpler. next-sitemap is more configurable. After deploying, validate your sitemap to catch formatting errors or missing URLs.
Block your API routes, admin pages, and any internal routes that should not be crawled. You can validate your robots.txt to confirm everything is configured correctly. Include the sitemap URL so crawlers can find it without relying on Search Console submission alone.
Structured data (JSON-LD)
Next.js does not have a built-in structured data solution. You add JSON-LD as a script tag in your page components. The standard approach is to create a component that renders a <script type="application/ld+json"> element with your serialized schema object. Since server components render this into the initial HTML, search engines see the structured data without executing JavaScript.
Create reusable components for each schema type you need: Article for blog posts, Product for e-commerce, Organization for your company pages, BreadcrumbList for navigation, FAQPage for FAQ sections.
You can validate your structured data implementation with the Ooty Schema Validator to catch errors before they reach production.
Image optimization with next/image
The next/image component handles responsive images, lazy loading, format conversion (WebP/AVIF), and sizing automatically. Use it for every content image on your site.
Always include descriptive alt text. The alt attribute is how search engines understand what an image shows. It is also essential for accessibility. Describe the image content. Do not stuff keywords.
Use the priority prop for above-the-fold images. By default, next/image lazy loads all images. For your LCP element (usually the hero image or main content image), add priority to tell the browser to load it immediately. This directly affects your Largest Contentful Paint score.
Canonical URLs
Set canonical URLs explicitly. If your site is accessible at both www and non-www, or if pages can be reached through multiple URL patterns, duplicate content issues will follow.
For dynamic pages, construct the canonical URL in generateMetadata. Use your production domain, not localhost or a preview deployment URL. A surprising number of Next.js sites have canonical tags pointing to Vercel preview URLs because the developer used process.env.VERCEL_URL instead of a hardcoded production domain.
Trailing slashes
Decide whether your URLs end with a trailing slash or not, then enforce it consistently. Next.js defaults to no trailing slash. If you want trailing slashes, set trailingSlash: true in next.config.js.
Inconsistency (some pages with, some without) creates duplicate content. Google treats /about and /about/ as different URLs unless you set canonical tags. Pick one format and stick with it across your entire site.
The rendering decision
This is where most Next.js SEO mistakes happen. Not in metadata or sitemaps, but in choosing the wrong rendering strategy for a given page.
Static Site Generation (SSG)
Best for: blog posts, documentation, marketing pages, landing pages. Any content that changes infrequently.
Pages are rendered at build time and served as static HTML from a CDN. Fastest possible load times. Search engines receive fully rendered HTML immediately. No server processing per request.
Use generateStaticParams to pre-render dynamic routes:
Best for: pages with user-specific content, search results, dashboards, pages where the content changes on every request.
Pages are rendered on the server for each request. Slower than SSG (the server has to do work per request), but the HTML is always current. Search engines still receive fully rendered HTML.
In the App Router, server components are SSR by default. To make a page dynamic (no caching), use export const dynamic = 'force-dynamic' or fetch data with { cache: 'no-store' }.
Incremental Static Regeneration (ISR)
Best for: product pages, frequently updated content, any page where you want SSG performance with regular content updates.
Pages are statically generated but regenerated in the background after a configurable time interval:
export const revalidate = 3600 // regenerate every hour
ISR gives you the CDN performance of SSG with near-real-time content freshness. For e-commerce sites with thousands of products, this is typically the right choice.
Client-side only rendering
Best for: nothing that needs to rank in search. Authenticated dashboards, internal tools, app features behind login.
This is the default behavior of plain React (Create React App). Content is rendered in the browser after JavaScript executes. Google can crawl JavaScript-rendered content, but it is deferred to a second wave of crawling, which may take days or weeks. For SEO content, never rely on client-side only rendering.
For a deeper explanation of how search engines process JavaScript, see our guide on JavaScript SEO.
Common mistakes
Not setting metadata on dynamic routes
Developers build a beautiful blog or product page, implement generateStaticParams, but forget generateMetadata. Every dynamic page gets the root layout's default metadata. Check your dynamic routes. Each one should have its own metadata function.
Missing OG images
Open Graph images affect click-through rates from social media and some search features. Next.js makes this easy with the opengraph-image.tsx convention (App Router) or the @vercel/og package for dynamic image generation.
At minimum, set a default OG image in your root layout. For content pages, generate images dynamically with the post title and your brand. The ImageResponse API lets you build OG images with JSX in a dedicated opengraph-image.tsx file alongside your page, and Next.js serves them automatically at the right URL.
Using hash routing
Hash-based routing (/#/about, /#/products) is invisible to search engines. The fragment identifier (everything after #) is never sent to the server and is not included in crawl requests. Next.js does not use hash routing by default, but developers sometimes introduce it through third-party routing libraries or custom navigation logic. If any of your SEO-relevant pages use hash routes, they will not be indexed.
Forgetting to handle www vs non-www
Configure your hosting to redirect one to the other. In Vercel, add both domains and set the canonical one as primary. The other will 308 redirect automatically. Without this, you might build backlinks to both versions, splitting your link equity between two duplicate versions of your entire site. After deploying, verify your redirects are returning the expected status codes.
The checklist
Every page has unique title and description via metadata or generateMetadata
Open Graph metadata set on all public pages
sitemap.ts (or next-sitemap) generating a complete sitemap
robots.ts blocking /api/ and internal routes
JSON-LD structured data on relevant page types
next/image with alt text on all images, priority on LCP images
Canonical URLs set explicitly using production domain
Trailing slash behavior consistent site-wide
Content pages use SSG or ISR, not client-side rendering
Dynamic routes have generateStaticParams and generateMetadata
OG images generated for content pages
www vs non-www redirects configured
Run your deployed pages through the Ooty SEO Analyzer to verify that server-rendered HTML includes your metadata, structured data, and content. It checks what search engines actually see, which may differ from what you see in your browser if client-side rendering is involved.
Next.js gives React developers a framework that search engines can work with natively. The technical infrastructure is there. The checklist above makes sure you are actually using it.
For a comparison of how other platforms handle these same challenges, see our Shopify SEO guide (e-commerce) and WordPress SEO guide (traditional CMS).