All guides
SEO18 min read

The Complete Next.js SEO Guide for 2026

A comprehensive playbook covering every SEO lever available in a modern Next.js application — from rendering strategy to structured data to Core Web Vitals.

NC

Nextcraft Agency

Why Next.js and SEO Are a Natural Fit

Next.js is the framework search engines have been waiting for. Server Components render complete HTML before the browser touches it. The Metadata API generates correct tags server-side without JavaScript. The sitemap and robots.txt APIs produce standards-compliant output from a single function.

The foundation is solid. But a solid foundation isn't a ranking — it's the minimum required to compete. This guide covers the full stack: what Next.js gives you for free, what you need to implement, and what requires an ongoing content strategy to win.


Part 1: Rendering for Crawlability

Server Components First

Every component in the App Router is a Server Component by default. This is the most important SEO decision built into the framework: your content is in the HTML response, not injected by JavaScript after the fact.

The practical rule: keep content-bearing components on the server. Move to Client Components only when you need interactivity.

code
// This content is in the HTML Googlebot receives on first fetch
async function ServiceList() {
  const services = await db.services.findMany();
  return (
    <ul>
      {services.map(s => (
        <li key={s.id}>
          <h3>{s.name}</h3>
          <p>{s.description}</p>
        </li>
      ))}
    </ul>
  );
}

Choosing the Right Data Caching Strategy

Next.js has four caching layers. For SEO, the relevant question is: is your content cached and served quickly?

Page typeCache strategyConfig
Static marketing pagesFull route cacheexport const revalidate = 3600
Blog postsISR with tag invalidationnext: { tags: ['posts'] }
Product listingsShort TTLnext: { revalidate: 300 }
User dashboardNo cachecache: 'no-store'

Slower pages cost crawl budget. Static and cached pages are served in milliseconds; fully dynamic pages require server processing per request.


Part 2: The Metadata System

Page-Level Metadata

Every page that can rank should have a unique metadata export:

code
// Static metadata
export const metadata: Metadata = {
  title: 'Next.js Development Agency | Nextcraft',
  description: 'We build high-performance Next.js applications for SaaS companies. Server Components, App Router, TypeScript. Contact us for a free consultation.',
  alternates: {
    canonical: 'https://nextcraft.agency/nextjs-development-agency',
  },
  openGraph: {
    title: 'Next.js Development Agency',
    description: 'High-performance Next.js applications for SaaS companies.',
    url: 'https://nextcraft.agency/nextjs-development-agency',
    type: 'website',
  },
};

For dynamic pages, use generateMetadata:

code
export async function generateMetadata({
  params,
}: {
  params: { slug: string };
}): Promise<Metadata> {
  const post = await getPost(params.slug);
  if (!post) return {};
  
  return {
    title: `${post.title} | Nextcraft`,
    description: post.excerpt,
    openGraph: {
      title: post.title,
      description: post.excerpt,
      type: 'article',
      publishedTime: post.publishedAt,
      authors: ['Nextcraft Team'],
    },
  };
}

Title Tag Formula

Good title tags have a consistent formula:

  • Landing pages: [Keyword] | [Brand]
  • Blog posts: [Post Title] | [Blog Name]
  • Category pages: [Category] Resources | [Brand]

Keep titles under 60 characters. Google truncates longer titles, and the truncated version is rarely as compelling as the full version.

Meta Descriptions

Meta descriptions don't directly affect rankings, but they do affect click-through rate — which affects organic traffic. Write them as ad copy:

  • State the primary benefit
  • Include a call to action
  • Keep under 155 characters
  • Match the search intent of the target keyword

Part 3: Technical SEO Implementation

Sitemap Generation

code
// app/sitemap.ts
import { MetadataRoute } from 'next';
import { getAllPosts, getAllServices } from '@/lib/content';

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const posts = await getAllPosts();
  const services = await getAllServices();
  
  return [
    {
      url: 'https://nextcraft.agency',
      lastModified: new Date(),
      changeFrequency: 'weekly',
      priority: 1.0,
    },
    ...posts.map(post => ({
      url: `https://nextcraft.agency/blog/${post.slug}`,
      lastModified: new Date(post.updatedAt),
      changeFrequency: 'monthly' as const,
      priority: 0.8,
    })),
    ...services.map(service => ({
      url: `https://nextcraft.agency/services/${service.slug}`,
      lastModified: new Date(),
      changeFrequency: 'monthly' as const,
      priority: 0.9,
    })),
  ];
}

Submit your sitemap to Google Search Console after deploying.

Canonical URLs

Set metadataBase in your root layout — all relative canonical URLs are resolved against it:

code
// app/layout.tsx
export const metadata: Metadata = {
  metadataBase: new URL('https://www.nextcraft.agency'),
};

For pages with multiple potential URLs (pagination, filtered views), always set an explicit canonical.

Robots.txt

code
// app/robots.ts
export default function robots(): MetadataRoute.Robots {
  return {
    rules: [
      {
        userAgent: '*',
        allow: '/',
        disallow: ['/api/', '/admin/'],
      },
    ],
    sitemap: 'https://www.nextcraft.agency/sitemap.xml',
  };
}

Part 4: Structured Data

Structured data is how you get rich results — enhanced SERP displays that increase click-through rate.

Organization Schema (Root Layout)

code
const schema = {
  "@context": "https://schema.org",
  "@type": "ProfessionalService",
  "name": "Nextcraft",
  "url": "https://nextcraft.agency",
  "description": "Next.js development agency specializing in SaaS products",
  "areaServed": "Worldwide",
  "priceRange": "$$",
};

Article Schema (Blog Posts)

code
const schema = {
  "@context": "https://schema.org",
  "@type": "TechArticle",
  "headline": post.title,
  "description": post.excerpt,
  "author": {
    "@type": "Organization",
    "name": "Nextcraft",
    "url": "https://nextcraft.agency"
  },
  "publisher": {
    "@type": "Organization",
    "name": "Nextcraft",
    "logo": {
      "@type": "ImageObject",
      "url": "https://nextcraft.agency/logo.png"
    }
  },
  "datePublished": post.publishedAt,
  "dateModified": post.updatedAt,
};

FAQ Schema (Service Pages)

code
const schema = {
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": faqs.map(faq => ({
    "@type": "Question",
    "name": faq.question,
    "acceptedAnswer": {
      "@type": "Answer",
      "text": faq.answer,
    },
  })),
};

Part 5: Core Web Vitals for SEO

Core Web Vitals are a confirmed ranking signal. Getting all three in the "Good" range doesn't guarantee rankings, but being in the "Poor" range is a documented disadvantage.

LCP (Largest Contentful Paint) — Target: under 2.5s

The fix for most sites: use next/image with priority on the above-the-fold hero image.

code
import Image from 'next/image';

<Image
  src="/hero.jpg"
  alt="Hero image"
  width={1200}
  height={600}
  priority  // Critical: prevents lazy-load delay on LCP element
/>

INP (Interaction to Next Paint) — Target: under 200ms

Minimize JavaScript on the main thread. Server Components reduce client-side JS automatically. For Client Components, use useTransition to mark non-urgent updates.

CLS (Cumulative Layout Shift) — Target: under 0.1

Use next/image (automatically reserves space), next/font (eliminates font-swap CLS), and explicit dimensions on any container that renders dynamic content.


Part 6: Image SEO

Images are indexed independently and can drive traffic from Google Images. Optimize them:

  • Alt text: Descriptive, includes the target keyword naturally. Not keyword-stuffed.
  • File names: nextjs-performance-dashboard.jpg beats IMG_2034.jpg
  • Formats: WebP served by default by next/image
  • Dimensions: Served at the actual display size, not oversized

Part 7: URL Structure

Good URL structure is readable, keyword-rich, and hierarchical.

PatternExampleNote
Category / slug/blog/nextjs-seo-guideStandard for blogs
Service parent/services/nextjs-developmentGood for service pages
Avoid dates/blog/2026/04/postDates age content
No query strings for canonical URLs/products not /products?view=gridSet canonical on paginated/filtered pages

Part 8: Content Principles for Rankings

Technical SEO is table stakes. Rankings come from content that satisfies search intent better than competitors.

Search Intent Matching

Before writing any piece of content, identify the intent behind the target keyword:

  • Informational: "What is Next.js App Router" → Write an educational explainer
  • Navigational: "Nextcraft agency" → Your homepage satisfies this
  • Commercial: "Next.js development agency pricing" → Pricing page or agency comparison guide
  • Transactional: "Hire Next.js developer" → Service page with clear CTA

Mismatching intent — writing an informational post for a transactional keyword — is the most common reason good content fails to rank.

Content Depth

Search engines increasingly reward depth: thoroughness, coverage of sub-topics, and answering questions that naturally arise from the main topic. This guide is an example of depth — it covers a broad topic from multiple angles.

For any target keyword, ask: what would someone who has read this article still want to know? If the answer is "a lot," the content isn't deep enough.

Freshness

Some queries reward fresh content (news, trends, "X in 2026" searches). Others don't care about date (evergreen how-tos, framework documentation). Know which type you're targeting and update your content accordingly.

For technical content: revisit annually at minimum. Outdated technical posts often rank poorly because engagement metrics (time on page, bounce rate) suffer when the content is stale.


Part 9: Monitoring and Iteration

Google Search Console

The non-negotiable SEO monitoring tool:

  • Submit sitemap on deploy
  • Monitor Index Coverage for crawl errors
  • Track Performance for rankings, impressions, and CTR by query
  • Use URL Inspection to debug individual pages

Core Web Vitals Report

In Search Console: Core Web Vitals → view the real-user data by page group. Focus on pages with "Poor" status first.

The Review Cycle

  • Weekly: Check for crawl errors and new indexed pages
  • Monthly: Review rankings for target keywords, identify content to update
  • Quarterly: Audit internal linking, identify pages that should be pruned or consolidated
  • Annually: Refresh evergreen content, update examples and statistics

SEO compounds. The work you do this month ranks in 3 months and continues contributing for years. The teams that win in organic search are the ones that build systematic processes, not the ones that do occasional bursts of optimization.

Stay Informed.

Join 1,200+ founders and engineers receiving our monthly deep dives on product engineering, design, and growth.

Insights once a month. No spam. Unsubscribe anytime.