Technical SEO Checklist for Next.js Applications
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
noindexon 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.
---