All posts
October 15, 20266 min readBy Ship Templates

The Ultimate Next.js SEO Guide: From Zero to Page One

A comprehensive, actionable guide to Next.js SEO covering metadata, structured data, Core Web Vitals, rendering strategies, sitemaps, and common pitfalls. Everything you need to rank your Next.js app.

next.jsseoreactweb performance

Next.js is one of the best frameworks for SEO, but shipping a Next.js app doesn't automatically mean good search rankings. You still need to handle metadata, structured data, rendering strategies, and performance correctly.

This guide covers everything you need to get your Next.js site from zero to page one.

Why Next.js is great for SEO

Unlike client-side React apps that send an empty HTML shell to the browser, Next.js can render pages on the server. That means search engine crawlers see your full content, links, and metadata without executing JavaScript.

Key advantages:

  • Server-side rendering (SSR) and static site generation (SSG) deliver complete HTML to crawlers
  • Built-in metadata API makes managing titles, descriptions, and Open Graph tags straightforward
  • Automatic code splitting keeps bundle sizes small and pages fast
  • Image optimization with next/image handles lazy loading, responsive sizes, and modern formats
  • File-based routing creates clean, crawlable URL structures

Setting up metadata with the Metadata API

Next.js 14+ has a powerful Metadata API that replaces the old Head component.

Static metadata

For pages where the metadata doesn't change, export a metadata object:

// app/about/page.tsx
import type { Metadata } from "next";

export const metadata: Metadata = {
  title: "About Us",
  description: "Learn about our team and mission.",
  openGraph: {
    title: "About Us | My App",
    description: "Learn about our team and mission.",
    type: "website",
  },
};

Dynamic metadata

For pages like blog posts or product pages, use generateMetadata:

// app/blog/[slug]/page.tsx
export async function generateMetadata({
  params,
}: {
  params: Promise<{ slug: string }>;
}): Promise<Metadata> {
  const { slug } = await params;
  const post = await getPost(slug);

  return {
    title: post.title,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      type: "article",
      publishedTime: post.date,
    },
  };
}

Title templates

Set a template in your root layout so every page gets a consistent suffix:

// app/layout.tsx
export const metadata: Metadata = {
  title: {
    default: "My App — Build Faster",
    template: "%s | My App",
  },
};

Now any child page that sets title: "About Us" will render as "About Us | My App".

Structured data (JSON-LD)

Structured data helps Google understand your content and can trigger rich results (review stars, FAQ dropdowns, breadcrumbs, etc.).

Add JSON-LD directly in your page components:

const jsonLd = {
  "@context": "https://schema.org",
  "@type": "Article",
  headline: post.title,
  datePublished: post.date,
  author: { "@type": "Person", name: post.author },
};

return (
  <>
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
    />
    <article>{/* content */}</article>
  </>
);

Common schema types to implement:

  • WebSite with SearchAction (enables sitelinks search box)
  • Organization with logo and sameAs
  • Article for blog posts
  • Product for product pages
  • FAQPage for FAQ sections
  • BreadcrumbList for navigation breadcrumbs

Rendering strategies and when to use each

Choosing the right rendering strategy directly affects SEO:

Static Site Generation (SSG)

Best for pages that rarely change: blog posts, documentation, landing pages.

  • Pre-rendered at build time
  • Served from CDN — fastest possible TTFB
  • Content is always crawlable

Server-Side Rendering (SSR)

Best for pages with frequently changing content: dashboards, search results, real-time data.

  • Rendered on each request
  • Always shows fresh data
  • Slightly slower TTFB than static

Incremental Static Regeneration (ISR)

Best of both worlds for content that changes periodically:

// Revalidate every 60 seconds
export const revalidate = 60;

Client-side only

Avoid for SEO-critical content. Content loaded via useEffect or client-side fetch won't be in the initial HTML that crawlers see.

Core Web Vitals optimization

Google uses Core Web Vitals as a ranking signal. Here's how to optimize each one in Next.js:

Largest Contentful Paint (LCP)

  • Use next/image with priority for above-the-fold images
  • Preload critical fonts
  • Avoid render-blocking third-party scripts
<Image src="/hero.jpg" alt="Hero" width={1200} height={630} priority />

Cumulative Layout Shift (CLS)

  • Always set width and height on images
  • Use placeholder="blur" on images
  • Reserve space for dynamic content with CSS

Interaction to Next Paint (INP)

  • Keep JavaScript bundles small
  • Use React.lazy and dynamic imports for heavy components
  • Avoid long-running synchronous tasks

Sitemaps and robots.txt

Next.js makes generating these easy with the App Router:

Sitemap

// app/sitemap.ts
import { MetadataRoute } from "next";

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const posts = await getAllPosts();

  return [
    { url: "https://example.com", lastModified: new Date(), priority: 1.0 },
    ...posts.map((post) => ({
      url: `https://example.com/blog/${post.slug}`,
      lastModified: post.updatedAt,
      priority: 0.8,
    })),
  ];
}

Robots.txt

// app/robots.ts
import { MetadataRoute } from "next";

export default function robots(): MetadataRoute.Robots {
  return {
    rules: { userAgent: "*", allow: "/", disallow: ["/api/", "/dashboard/"] },
    sitemap: "https://example.com/sitemap.xml",
  };
}

Canonical URLs

Canonical tags prevent duplicate content issues. Set them in your metadata:

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

Important canonical rules:

  • Paginated pages should self-canonicalize (/?page=2 → /?page=2)
  • Search/filter pages should canonicalize to the clean listing
  • Always use absolute URLs

Open Graph and social previews

Make your pages look great when shared on social media:

// app/opengraph-image.tsx
import { ImageResponse } from "next/og";

export const alt = "My App";
export const size = { width: 1200, height: 630 };
export const contentType = "image/png";

export default async function Image() {
  return new ImageResponse(
    <div style={{ /* your design */ }}>
      <h1>My Page Title</h1>
    </div>,
    { ...size }
  );
}

Common Next.js SEO mistakes

  1. Using client-side rendering for important content — If crawlers can't see it, it doesn't exist for SEO
  2. Missing canonical tags — Leads to duplicate content across pagination, filters, and query parameters
  3. Not setting image alt text — Missed opportunity for image search traffic
  4. Blocking crawlers from JavaScript/CSS — Check your robots.txt isn't too restrictive
  5. Ignoring Core Web Vitals — Performance is a ranking signal; test with PageSpeed Insights
  6. Using generic meta descriptions — Each page needs a unique, compelling description
  7. Not submitting a sitemap to Google Search Console — Don't wait for Google to discover your pages

Next.js SEO checklist

Use this checklist for every page:

  • Unique, descriptive title under 60 characters
  • Compelling meta description under 160 characters
  • Canonical URL set
  • Open Graph and Twitter meta tags
  • Structured data (JSON-LD) where applicable
  • All images have descriptive alt text
  • Images use next/image with proper sizing
  • Page loads in under 2.5 seconds (LCP)
  • No layout shifts (CLS < 0.1)
  • Internal links to related content
  • Included in sitemap.xml
  • Mobile-responsive layout

Start with a template

Building all of this from scratch takes time. A production-ready Next.js template gives you metadata, structured data, sitemaps, and performance optimization already wired up so you can focus on your product instead of your SEO plumbing.

Browse Next.js templates →

Ready to ship faster?

Browse production-ready web framework templates and starter kits.

Browse templates