nextjs-seo

安装量: 468
排名: #2212

安装

npx skills add https://github.com/laguagu/claude-code-nextjs-skills --skill nextjs-seo

Comprehensive SEO guide for Next.js 16+ applications using App Router.

Version: Updated for Next.js 16.1.3 (January 2026)

Quick SEO Audit

Run this checklist for any Next.js project:

  • Check robots.txt: curl https://your-site.com/robots.txt

  • Check sitemap: curl https://your-site.com/sitemap.xml

  • Check metadata: View page source, search for <title> and <meta name="description">

  • Check JSON-LD: View page source, search for application/ld+json

  • Check Core Web Vitals: Run Lighthouse in Chrome DevTools

Essential Files

app/layout.tsx - Root Metadata

import type { Metadata, Viewport } from 'next';

// Viewport (separate export required in Next.js 14+)
export const viewport: Viewport = {
  width: 'device-width',
  initialScale: 1,
  maximumScale: 5,
  userScalable: true,
  themeColor: [
    { media: '(prefers-color-scheme: light)', color: '#ffffff' },
    { media: '(prefers-color-scheme: dark)', color: '#0a0a0a' },
  ],
};

export const metadata: Metadata = {
  metadataBase: new URL('https://your-site.com'),
  title: {
    default: 'Site Title - Main Keyword',
    template: '%s | Site Name',
  },
  description: 'Compelling description with keywords (150-160 chars)',
  keywords: ['keyword1', 'keyword2', 'keyword3'],
  openGraph: {
    type: 'website',
    locale: 'en_US',
    url: 'https://your-site.com',
    siteName: 'Site Name',
    title: 'Site Title',
    description: 'Description for social sharing',
    images: [{ url: '/og-image.png', width: 1200, height: 630, alt: 'Site preview' }],
  },
  twitter: {
    card: 'summary_large_image',
    title: 'Site Title',
    description: 'Description for Twitter',
    images: ['/og-image.png'],
  },
  alternates: {
    canonical: '/',
  },
  robots: {
    index: true,
    follow: true,
  },
};

app/sitemap.ts - Dynamic Sitemap

import type { MetadataRoute } from 'next';

export default function sitemap(): MetadataRoute.Sitemap {
  const baseUrl = 'https://your-site.com';

  return [
    {
      url: baseUrl,
      lastModified: new Date(),
      changeFrequency: 'weekly',
      priority: 1,
      images: [`${baseUrl}/og-image.png`], // Next.js 16 Image Sitemap
    },
    {
      url: `${baseUrl}/about`,
      lastModified: new Date(),
      changeFrequency: 'monthly',
      priority: 0.8,
    },
  ];
}

app/robots.ts - Robots Configuration

import type { MetadataRoute } from 'next';

export default function robots(): MetadataRoute.Robots {
  const baseUrl = 'https://your-site.com';

  return {
    rules: [
      {
        userAgent: '*',
        allow: '/',
        disallow: ['/api/', '/_next/', '/admin/'],
      },
    ],
    sitemap: `${baseUrl}/sitemap.xml`,
    host: baseUrl,
  };
}

Key Principles

Rendering Strategy for SEO

| SSG (Static) | Content rarely changes | Best - pre-rendered HTML

| SSR | Dynamic content per request | Great - server-rendered

| ISR | Large sites, periodic updates | Great - cached + fresh

| CSR | Dashboards, authenticated areas | Poor - avoid for SEO pages

Core Web Vitals Targets

| LCP (Largest Contentful Paint) | < 2.5s | Loading speed

| INP (Interaction to Next Paint) | < 200ms | Interactivity

| CLS (Cumulative Layout Shift) | < 0.1 | Visual stability

References

Common Mistakes to Avoid

  • Mixing next-seo with Metadata API - Use only Metadata API in App Router

  • Missing canonical URLs - Always set alternates.canonical

  • Using CSR for SEO pages - Use SSG/SSR for indexable content

  • Blocking assets in robots.txt - Don't block CSS/JS needed for rendering

  • Missing metadataBase - Required for relative URLs in metadata

  • Viewport in metadata - Must be separate export in Next.js 14+

  • Mixing metadata object and generateMetadata - Use one or the other, not both

Quick Fixes

Add noindex to a page

export const metadata: Metadata = {
  robots: {
    index: false,
    follow: false,
  },
};

Dynamic metadata per page

export async function generateMetadata({ params }): Promise<Metadata> {
  const product = await getProduct(params.id);
  return {
    title: product.name,
    description: product.description,
  };
}

Canonical for dynamic routes

export async function generateMetadata({ params }): Promise<Metadata> {
  return {
    alternates: {
      canonical: `/products/${params.slug}`,
    },
  };
}
返回排行榜