SEO for Next.js 2026: Rank on Google, Appear in AI Search, and Google Discover

Sanjeev SharmaSanjeev Sharma
5 min read

Advertisement

SEO for Next.js 2026: Rank Everywhere

SEO in 2026 means three battlefields: Google Search, Google Discover, and AI engines (ChatGPT, Perplexity, Gemini). This guide covers all three.

Metadata API (Next.js 15)

// app/layout.tsx — site-wide defaults
import type { Metadata } from 'next'

export const metadata: Metadata = {
  metadataBase: new URL('https://webcoderspeed.com'),
  title: {
    default: 'WebCoderSpeed — Master Programming & AI',
    template: '%s | WebCoderSpeed',
  },
  description: 'Master DSA, AI, web development and system design. Daily tutorials on Python, JavaScript, React, Next.js, LLMs, and FAANG interview prep.',
  keywords: ['programming', 'DSA', 'AI', 'Next.js', 'React', 'Python', 'LLM'],
  authors: [{ name: 'Sanjeev Sharma', url: 'https://webcoderspeed.com' }],

  openGraph: {
    type: 'website',
    siteName: 'WebCoderSpeed',
    locale: 'en_US',
    images: [{ url: '/og-image.png', width: 1200, height: 630 }],
  },

  twitter: {
    card: 'summary_large_image',
    site: '@webcoderspeed',
    creator: '@webcoderspeed',
  },

  robots: {
    index: true,
    follow: true,
    googleBot: { index: true, follow: true, 'max-image-preview': 'large' },
  },

  alternates: { canonical: 'https://webcoderspeed.com' },
}

Dynamic Metadata for Blog Posts

// app/blog/[slug]/page.tsx
import type { Metadata } from 'next'
import { getPost } from '@/lib/posts'

export async function generateMetadata(
  { params }: { params: { slug: string } }
): Promise<Metadata> {
  const post = await getPost(params.slug)
  if (!post) return { title: 'Post Not Found' }

  const url = `https://webcoderspeed.com/blog/${params.slug}`

  return {
    title: post.title,
    description: post.summary,
    keywords: post.tags,

    openGraph: {
      type: 'article',
      title: post.title,
      description: post.summary,
      url,
      images: [
        {
          url: post.image || '/og-default.png',
          width: 1200,
          height: 630,
          alt: post.title,
        },
      ],
      publishedTime: post.date,
      authors: ['https://webcoderspeed.com/about'],
      tags: post.tags,
    },

    alternates: { canonical: url },
  }
}

JSON-LD Structured Data

Structured data helps Google understand your content AND helps AI engines cite you accurately:

// components/article-schema.tsx
export function ArticleSchema({ post }: { post: Post }) {
  const schema = {
    '@context': 'https://schema.org',
    '@type': 'TechArticle',
    headline: post.title,
    description: post.summary,
    image: `https://webcoderspeed.com${post.image || '/og-default.png'}`,
    datePublished: post.date,
    dateModified: post.updatedAt || post.date,
    author: {
      '@type': 'Person',
      name: 'Sanjeev Sharma',
      url: 'https://webcoderspeed.com/about',
    },
    publisher: {
      '@type': 'Organization',
      name: 'WebCoderSpeed',
      logo: {
        '@type': 'ImageObject',
        url: 'https://webcoderspeed.com/logo.png',
      },
    },
    mainEntityOfPage: { '@type': 'WebPage', '@id': `https://webcoderspeed.com/blog/${post.slug}` },
    keywords: post.tags.join(', '),
    articleSection: post.category,
  }

  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }}
    />
  )
}

// BreadcrumbList
export function BreadcrumbSchema({ items }: { items: { name: string; url: string }[] }) {
  const schema = {
    '@context': 'https://schema.org',
    '@type': 'BreadcrumbList',
    itemListElement: items.map((item, index) => ({
      '@type': 'ListItem',
      position: index + 1,
      name: item.name,
      item: item.url,
    })),
  }
  return <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }} />
}

// FAQ Schema (gets rich snippets in search)
export function FAQSchema({ questions }: { questions: { q: string; a: string }[] }) {
  const schema = {
    '@context': 'https://schema.org',
    '@type': 'FAQPage',
    mainEntity: questions.map(({ q, a }) => ({
      '@type': 'Question',
      name: q,
      acceptedAnswer: { '@type': 'Answer', text: a },
    })),
  }
  return <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }} />
}

Sitemap

// app/sitemap.ts
import type { MetadataRoute } from 'next'
import { getAllPosts } from '@/lib/posts'

export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
  const posts = await getAllPosts()
  const baseUrl = 'https://webcoderspeed.com'

  const staticRoutes: MetadataRoute.Sitemap = [
    { url: baseUrl, lastModified: new Date(), changeFrequency: 'daily', priority: 1 },
    { url: `${baseUrl}/blog`, lastModified: new Date(), changeFrequency: 'daily', priority: 0.9 },
    { url: `${baseUrl}/about`, changeFrequency: 'monthly', priority: 0.5 },
  ]

  const postRoutes: MetadataRoute.Sitemap = posts.map(post => ({
    url: `${baseUrl}/blog/${post.slug}`,
    lastModified: new Date(post.date),
    changeFrequency: 'monthly',
    priority: 0.8,
  }))

  return [...staticRoutes, ...postRoutes]
}

Robots.txt

// app/robots.ts
import type { MetadataRoute } from 'next'

export default function robots(): MetadataRoute.Robots {
  return {
    rules: [
      {
        userAgent: '*',
        allow: '/',
        disallow: ['/api/', '/admin/', '/_next/'],
      },
      // Allow AI crawlers explicitly
      { userAgent: 'GPTBot', allow: '/' },
      { userAgent: 'ClaudeBot', allow: '/' },
      { userAgent: 'PerplexityBot', allow: '/' },
      { userAgent: 'GoogleBot', allow: '/' },
    ],
    sitemap: 'https://webcoderspeed.com/sitemap.xml',
    host: 'https://webcoderspeed.com',
  }
}

Google Discover Optimization

Google Discover shows content without searching. To appear:

  1. Large featured image — minimum 1200px wide, max-image-preview: large
  2. Fresh content — publish frequently with today's date
  3. Compelling title — not clickbait but genuinely interesting
  4. E-E-A-T signals — Experience, Expertise, Authority, Trust
  5. Fast page — LCP < 2.5s, CLS < 0.1, INP < 200ms
// Ensure Discover eligibility in metadata
export const metadata: Metadata = {
  robots: {
    googleBot: {
      'max-image-preview': 'large',  // Required for Discover
      'max-snippet': -1,
      'max-video-preview': -1,
    },
  },
}

AI Engine Optimization

For Perplexity, ChatGPT, and Gemini to cite your content:

1. Write definitively — "X is Y" not "X might be Y"
2. Use clear headings — AI extracts structured sections
3. Include code examples — AI engines love citing working code
4. Add FAQ sections — exactly what AI extracts for answers
5. Be comprehensive — AI engines prefer complete, authoritative content
6. Allow crawling — GPTBot, ClaudeBot, PerplexityBot in robots.txt
7. Use structured data — JSON-LD helps AI understand context

Internal Linking Strategy

// Automatic related posts component
async function RelatedPosts({ tags, currentSlug }: { tags: string[]; currentSlug: string }) {
  const related = await getAllPosts()
    .then(posts => posts
      .filter(p => p.slug !== currentSlug && p.tags.some(t => tags.includes(t)))
      .sort((a, b) => {
        const aScore = a.tags.filter(t => tags.includes(t)).length
        const bScore = b.tags.filter(t => tags.includes(t)).length
        return bScore - aScore
      })
      .slice(0, 3)
    )

  return (
    <section>
      <h3>Related Articles</h3>
      {related.map(post => (
        <a key={post.slug} href={`/blog/${post.slug}`}>{post.title}</a>
      ))}
    </section>
  )
}

SEO is a compound investment. Every post you publish, every schema you add, every internal link you create — it compounds. Start today, stay consistent.

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro