CCelestiallines.com
← Back to blog

Next.js SEO Best Practices for Better Search Rankings

By Pawan Singh · Jun 2026

Next.js removes most of the historical reasons a React app struggled with SEO, but only for the parts you actually configure — a Next.js app rendered entirely client-side with no metadata configured has the same SEO problems a plain React SPA would.

Render Content Server-Side, Not Client-Only

The single biggest SEO lever: make sure the content you want indexed is actually present in the server-rendered HTML, not assembled client-side after a useEffect fetch. In the App Router, Server Components (the default) already render server-side by nature — the risk is marking something 'use client' unnecessarily and then fetching its content client-side, which puts you back in classic SPA-indexing territory for that content.

The Metadata API

Next.js's built-in Metadata API generates <title>, <meta>, and Open Graph tags without manually managing a document head:

export const metadata = {
    title: 'Article Title',
    description: 'A concise, accurate summary of the page.',
    openGraph: {
        title: 'Article Title',
        images: ['/og-image.jpg'],
    },
};

For pages needing per-content dynamic metadata (a blog post whose title/description depends on the fetched article), use generateMetadata() instead of a static export — it's an async function that runs server-side with access to the route's params, letting each article get its own accurate title and description rather than one generic site-wide value repeated everywhere.

Canonical URLs

export const metadata = {
    alternates: { canonical: 'https://example.com/blog/my-post' },
};

Set this explicitly on any content reachable by more than one URL (with and without a trailing slash, via a query parameter variant, or a paginated listing) — without it, search engines may index multiple URLs for what's really the same content and split ranking signal between them instead of consolidating it onto one canonical URL.

Structured Data (JSON-LD)

Embed structured data directly in the page for rich search results (article publish dates, breadcrumbs, product pricing):

<script
    type="application/ld+json"
    dangerouslySetInnerHTML={{
        __html: JSON.stringify({
            '@context': 'https://schema.org',
            '@type': 'Article',
            headline: post.title,
            datePublished: post.publishedAt,
        }),
    }}
/>

This doesn't directly boost rankings, but it enables richer search result presentation (star ratings, publish dates shown in the SERP itself), which meaningfully affects click-through rate even at the same ranking position.

The sitemap.ts and robots.ts Conventions

Next.js supports generating both directly as code instead of static files, which matters for any site with dynamic content:

// app/sitemap.ts
export default async function sitemap() {
    const posts = await getPosts();
    return posts.map((post) => ({
        url: `https://example.com/blog/${post.slug}`,
        lastModified: post.updatedAt,
    }));
}

For a sitemap backed by data that changes independently of a deploy (a headless CMS, a database), add export const dynamic = 'force-dynamic' — otherwise a sitemap built from a data source outside the app's own build process can get statically generated once at build time and never reflect content added afterward.

Don't Rely on the Sitemap Alone for Discoverability

A sitemap is a weak signal on its own — every page it lists should also be reachable through real, crawlable <a href> links from other indexed pages. Content that exists only in the sitemap, with no internal links pointing to it (a common failure mode of client-side-only pagination or filtering), is far more likely to sit "Discovered — not indexed" indefinitely, regardless of how complete the sitemap is.

Core Web Vitals

Search ranking factors include real-world loading and interactivity performance (Largest Contentful Paint, Interaction to Next Paint, Cumulative Layout Shift). Next.js's built-in <Image> component (preventing layout shift and lazy-loading off-screen images) and font optimization (avoiding a layout-shifting font swap) both directly target these metrics rather than being purely cosmetic conveniences.

Common Issues

  • Pages showing in Google as "Crawled — currently not indexed" — often a content quality/depth signal rather than a technical bug; verify the page's actual rendered content is substantial and unique before assuming it's a crawlability issue.
  • Duplicate content across paginated or filtered URLs — missing canonical tags pointing back to a primary URL.
  • Metadata identical across many pages — a static metadata export used where generateMetadata() was needed for per-page uniqueness.
  • Sitemap stuck showing stale content — missing force-dynamic on a sitemap backed by a database or CMS.

Need help getting a Next.js site's SEO fundamentals right? Get in touch — I work as a freelance Next.js developer.

Comments