Programmatic SEO with Next.js: What Works & What Breaks
Spent 6 months building pSEO pages in Next.js. Here's what actually works (and what breaks):
What is Programmatic SEO (pSEO)?
Programmatic SEO is automatically generating hundreds or thousands of SEO-optimized pages from a template and database. Instead of manually writing each page, you create one template and populate it with data.
Classic Examples:
- Zillow: Millions of pages like "Homes for sale in [City], [State]"
- Nomad List: "Best cities for [job type]"
- TripAdvisor: "[Activity] in [Location]"
- MoneySense AI: "[this] vs [that] financial term explained"
You can checkout MoneySense AI - Learn Page
Each page targets a specific long-tail keyword, has unique content, and ranks independently in search results.
Why Next.js is Perfect for pSEO
1. Static Site Generation (SSG) with Dynamic Routes
Next.js pre-renders every page as static HTML. Google sees fully-rendered content instantly - no client-side rendering issues.
// Generate 10,000 pages at build time
export async function generateStaticParams() {
const calculations = await db.getAllCalculatorTypes();
return calculations.map(calc => ({ slug: calc.slug }));
}
2. Incremental Static Regeneration (ISR)
Game-changer alert! Generate 10,000+ pages at build time, update them on-demand. We're using this for MoneySense calculators - each financial tool gets its own optimized page.
You don't need to rebuild your entire site when data changes. ISR regenerates individual pages on-demand:
export const revalidate = 86400; // Update every 24 hours
This is crucial for pSEO - you can have 50,000 pages without 2-hour build times.
3. Built-in SEO Optimization
App Router's metadata API makes SEO trivial. Dynamic meta tags, structured data, Open Graph - all type-safe and colocated with your page logic. No more fighting with react-helmet.
export const metadata = {
title: "`${calculatorName} - Free Online Calculator`,"
description: "`Calculate ${calculatorName}...`,"
openGraph: {
title: "`${calculatorName} Calculator`,"
description: "`Free online ${calculatorName} calculator`,"
images: ['/og-image.jpg'],
}
}
4. Server Components (App Router)
Fetch data directly in your component without client-side overhead. Zero JavaScript sent to client for static content. Faster page loads = better SEO rankings.
async function CalculatorPage({ params }) {
const data = await getCalculatorData(params.slug);
return <Calculator data={data} />;
}
5. Edge Rendering
Edge functions let you personalize content by location without sacrificing SEO. Serve UK-specific financial content to UK users while keeping everything server-rendered.
export const runtime = 'edge';
export default function Page({ searchParams }) {
const country = headers().get('x-vercel-ip-country') || 'US';
const currency = country === 'GB' ? '£' : '$';
return <Calculator currency={currency} />;
}
UK user sees "£" and UK tax rates. US user sees "$" and US rates. Same URL, different content, all server-rendered.
The Pain Points
Build Times Explode Fast
50k pages = 45-minute builds even with caching.
Solution: Generate most popular pages statically, rest on-demand with ISR revalidation.
export async function generateStaticParams() {
// Only generate top 1000 pages at build time
const topPages = await db.getTopPages(1000);
return topPages.map(page => ({ slug: page.slug }));
}
// Rest will be generated on-demand with ISR
export const dynamicParams = true;
export const revalidate = 86400;
Database Costs Spike Hard
Every page generation hits your DB. We shifted to edge caching with Vercel KV to avoid destroying our Postgres bill.
import { kv } from '@vercel/kv';
async function getCalculatorData(slug) {
const cached = await kv.get(`calc:${slug}`);
if (cached) return cached;
const data = await db.getCalculator(slug);
await kv.set(`calc:${slug}`, data, { ex: 86400 });
return data;
}
Duplicate Content Risks Are Real
If you're auto-generating pages, you need robust canonicalization and noindex strategies. Google will penalize thin content.
export const metadata = {
robots: {
index: pageQualityScore > 7, // Only index high-quality pages
follow: true,
},
alternates: {
canonical: `https://example.com/${slug}`,
}
}
Architecture That Works
Here's our proven workflow:
- Seed your DB with target keywords
- Generate page templates with unique H1s/titles
- Use ISR with 24hr revalidation
- Monitor GSC for thin content flags
- Prune low-performing pages
// Example: Automated quality monitoring
async function prunePages() {
const pages = await db.getAllPages();
for (const page of pages) {
const analytics = await getGSCData(page.url);
if (analytics.impressions < 100 && analytics.clicks < 5) {
// Mark as noindex after 90 days
await db.updatePage(page.id, { noindex: true });
}
}
}
Why Not Other Frameworks?
| Framework | Pros | Cons |
|---|---|---|
| WordPress/PHP | Easy to start | Slow, database-heavy, doesn't scale to 10k+ pages efficiently |
| Pure React (CRA) | Familiar | Client-side rendering kills SEO. Google has to execute JavaScript to see content |
| Gatsby | Good SSG | Build times are brutal for large-scale pSEO. 50k pages = hours of building |
| Astro | Fast, simple | Good alternative, but Next.js has better data fetching patterns and middleware for personalization |
Real Results
AllProCalculator Case Study
Our AllProCalculator project went from 0 to 12k monthly organic visits in 4 months using this approach.
Stack:
- Template: "Calculate [X]" pages
- Data: 200+ calculator types in PostgreSQL
- Build: ISR with 24hr revalidation
- Hosting: Vercel with edge caching
Key Metrics:
- 200+ indexed pages
- Average page load: 0.8s
- Core Web Vitals: All green
- 12,000 monthly organic visits
Is It Worth It?
Absolutely - if you have genuine unique content for each page.
Not worth it if you're just keyword stuffing templated pages.
Next.js 14's partial prerendering could solve the build time issue, but it's still experimental.
The combination of SSG performance + ISR flexibility + built-in SEO tools makes Next.js the best choice for programmatic SEO at scale.
Getting Started
Ready to build your own pSEO site? Here's a quick starter:
npx create-next-app@latest my-pseo-site
cd my-pseo-site
// app/[slug]/page.js
export async function generateStaticParams() {
const items = await db.getAllItems();
return items.map(item => ({ slug: item.slug }));
}
export async function generateMetadata({ params }) {
const item = await db.getItem(params.slug);
return {
title: `${item.title} - Your Site`,
description: item.description,
};
}
export default async function Page({ params }) {
const item = await db.getItem(params.slug);
return <Article data={item} />;
}
export const revalidate = 86400; // 24 hours
Start small, validate your content quality, then scale up. Monitor Google Search Console religiously for thin content warnings.
Building pSEO at scale? I'd love to hear about your challenges. Drop a comment below!
Top comments (0)