Skip to content
    2025-09-01|5 min read

    Technical SEO Checklist for Next.js Applications

    #seo#nextjs#metadata#sitemap#structured-data

    Next.js is an excellent framework for SEO, but "excellent out of the box" doesn't mean "optimal without configuration." Search engines need explicit signals to understand, index, and rank your content. Missing even a single element — like a canonical URL or a proper sitemap — can silently hurt your rankings.

    This checklist covers every technical SEO element you need in a production Next.js application. I use this on every project I build, including ProfitPlate which was designed with SEO as a core architectural requirement.

    1. Metadata Management

    Next.js 14+ uses the Metadata API for all SEO metadata. Define a root layout with defaults, then override per-page.

    ```typescript // app/layout.tsx import type { Metadata } from 'next';

    export const metadata: Metadata = { metadataBase: new URL('https://www.rahulsinghnegi.com'), title: { default: 'Rahul Singh Negi — Software Developer', template: '%s | Rahul Singh Negi', }, description: 'Freelance full-stack developer specializing in Next.js, React, and Firebase.', openGraph: { type: 'website', locale: 'en_US', url: '/', siteName: 'Rahul Singh Negi', images: [{ url: '/images/og-default.jpg', width: 1200, height: 630 }], }, twitter: { card: 'summary_large_image', creator: '@rahulsinghnegi', }, robots: { index: true, follow: true, }, }; ```

    Per-page metadata overrides the template:

    ``typescript // app/blog/[slug]/page.tsx export async function generateMetadata({ params }: Props): Promise<Metadata> { const post = await getPost(params.slug); return { title: post.title, description: post.excerpt, alternates: { canonical: /blog/${post.slug} }, openGraph: { title: post.title, description: post.excerpt, images: [{ url: post.coverImage, width: 1200, height: 630 }], }, }; } ``

    2. Dynamic Sitemap Generation

    Every indexable page must appear in the sitemap. Next.js makes this trivial with the sitemap route handler:

    ```typescript // app/sitemap.ts import { getBlogPosts } from '@/lib/content'; import type { MetadataRoute } from 'next';

    export default async function sitemap(): Promise<MetadataRoute.Sitemap> { const baseUrl = 'https://www.rahulsinghnegi.com';

    const staticPages = [ { url: baseUrl, lastModified: new Date(), changeFrequency: 'monthly', priority: 1.0 }, { url: ${baseUrl}/about, lastModified: new Date(), changeFrequency: 'monthly', priority: 0.8 }, { url: ${baseUrl}/services, lastModified: new Date(), changeFrequency: 'monthly', priority: 0.9 }, { url: ${baseUrl}/blog, lastModified: new Date(), changeFrequency: 'weekly', priority: 0.9 }, { url: ${baseUrl}/contact, lastModified: new Date(), changeFrequency: 'monthly', priority: 0.7 }, ];

    const blogPosts = await getBlogPosts(); const blogEntries = blogPosts.map((post) => ({ url: ${baseUrl}/blog/${post.slug}, lastModified: new Date(post.date), changeFrequency: 'monthly' as const, priority: 0.8, }));

    return [...staticPages, ...blogEntries]; } ```

    Submit the sitemap URL in Google Search Console. For large sites (50,000+ URLs), split into multiple sitemap files.

    3. Canonical URLs

    Canonical URLs prevent duplicate content issues. Every page should have a self-referencing canonical:

    ``typescript export const metadata = { alternates: { canonical: '/current-page-path', }, }; ``

    For query parameters that don't change content (like ?utm_source=twitter), the canonical URL strips them automatically — Next.js's Metadata API handles this correctly.

    4. Structured Data (JSON-LD)

    Structured data enables rich results — the enhanced search listings that dramatically improve click-through rates. I cover this in detail in my Structured Data and JSON-LD guide, but here's the essential schema for a blog:

    ``tsx // components/JsonLd.tsx export function BlogPostJsonLd({ post }: { post: BlogPost }) { const jsonLd = { '@context': 'https://schema.org', '@type': 'BlogPosting', headline: post.title, description: post.excerpt, image: post.coverImage, datePublished: post.date, dateModified: post.updatedAt || post.date, author: { '@type': 'Person', name: 'Rahul Singh Negi', url: 'https://www.rahulsinghnegi.com/about', }, publisher: { '@type': 'Person', name: 'Rahul Singh Negi', }, mainEntityOfPage: { '@type': 'WebPage', '@id': https://www.rahulsinghnegi.com/blog/${post.slug}`, }, };

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

    5. Robots.txt

    Control crawler access with a dynamic robots.txt:

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

    export default function robots(): MetadataRoute.Robots { return { rules: [ { userAgent: '*', allow: '/', disallow: ['/api/', '/admin/', '/_next/', '/404'], }, { userAgent: 'GPTBot', disallow: '/', }, ], sitemap: 'https://www.rahulsinghnegi.com/sitemap.xml', }; } ```

    6. Open Graph and Social Cards

    Social previews drive click-through from LinkedIn, Twitter, and Facebook. Generate social card images dynamically:

    ```typescript // app/api/og/route.tsx import { ImageResponse } from 'next/og';

    export async function GET(req: Request) { const { searchParams } = new URL(req.url); const title = searchParams.get('title') || 'Default title';

    return new ImageResponse( ( <div style={{ background: '#0f172a', width: 1200, height: 630, display: 'flex', flexDirection: 'column', justifyContent: 'center', padding: 60 }}> <h1 style={{ color: 'white', fontSize: 64, fontWeight: 700 }}>{title}</h1> <p style={{ color: '#94a3b8', fontSize: 32 }}>rahulsinghnegi.com</p> </div> ), { width: 1200, height: 630 } ); } ```

    7. Core Web Vitals

    SEO and performance are inseparable. Ensure your application optimizes LCP, FID, and CLS as described in my Performance Optimization guide. Google's ranking algorithms directly incorporate these metrics.

    Verification Checklist

    Before launch, run through this list:

    • [ ] Root metadata defined with defaults
    • [ ] Every page has unique title and description
    • [ ] Canonical URL set on every page
    • [ ] Sitemap generates dynamically and includes all indexable pages
    • [ ] Robots.txt disallows admin and API routes
    • [ ] Structured data present on blog posts, services, and about page
    • [ ] Open Graph tags set with correct images
    • [ ] Social card image generation configured
    • [ ] Google Search Console verified and sitemap submitted
    • [ ] Lighthouse SEO score 100
    • [ ] No noindex on pages that should be indexed

    Conclusion

    Technical SEO is not a one-time setup — it's an ongoing practice. Every new page, every content update, every deployment should pass through this checklist. Next.js provides the infrastructure; your job is to use it thoroughly.

    Need an SEO audit of your Next.js site? Get in touch and I'll analyze your metadata, structured data, and crawlability.

    ---

    R

    Written by

    Rahul

    Freelance developer for startups building SaaS products, MVPs, mobile apps, and conversion-focused website improvements.

    Building something?

    I am currently available for new projects. Share your idea and I will give you an honest assessment, delivery plan, and quote.