# Programmatic SEO with Next.js: Building 1000 Pages That Rank
By Govind Bajaj · 2025

Tags: seo, programmatic-seo, next.js, ssg, scaling
I built 847 location-specific pages for a client. They generated 12,000 monthly organic visits within 3 months. No manual writing. Just data, templates, and Next.js.
I built 847 location-specific landing pages for a client project last year.

Each page targeted a specific city plus service combination like web development in Mumbai or web development in Delhi. They were generated from a template and a CSV file. No page was written manually.

Within three months, those pages generated 12,000 monthly organic visits. The total development time was two days.

This is programmatic SEO — using code to generate pages at scale. And Next.js is the ideal platform for it.

## What Programmatic SEO Actually Means

Programmatic SEO is the practice of generating landing pages from structured data using templates. Instead of writing 100 pages manually, you write one template and generate 100 pages from a dataset.

It works when you have a repeatable page structure, structured data that varies per page like locations or products or categories, and search demand for the specific combinations.

It fails when you generate thin content — pages with no unique value. Google penalizes mass-generated low-quality pages. The key is generating useful pages at scale.

## The Architecture: Data Plus Template Equals Pages

For the location pages, I used this structure:

```
data/
├── locations.csv       # 847 cities with population, state, coordinates
└── services.csv        # 12 service types

app/
├── [location]/
│   └── [service]/
│       └── page.tsx    # Single template, generates all combinations
```

## The Template Component

```tsx
// app/[location]/[service]/page.tsx
import { notFound } from 'next/navigation'
import { getLocation, getService } from '@/lib/data'
import { Schema } from '@/components/Schema'

export async function generateStaticParams() {
  const locations = await getLocations()
  const services = await getServices()
  
  const params = []
  for (const loc of locations) {
    for (const svc of services) {
      params.push({
        location: slugify(loc.city),
        service: slugify(svc.name)
      })
    }
  }
  return params
}

export async function generateMetadata({ params }) {
  const location = await getLocation(params.location)
  const service = await getService(params.service)
  
  if (!location || !service) return {}
  
  return {
    title: `${service.name} in ${location.city}, ${location.state}`,
    description: `Professional ${service.name.toLowerCase()} services in ${location.city}. Serving ${location.population.toLocaleString()} residents across ${location.state}.`,
  }
}

export default async function ServiceLocationPage({ params }) {
  const location = await getLocation(params.location)
  const service = await getService(params.service)
  
  if (!location || !service) notFound()
  
  return (
    <div>
      <Schema
        data={{
          '@type': 'Service',
          name: `${service.name} in ${location.city}`,
          areaServed: {
            '@type': 'City',
            name: location.city
          }
        }}
      />
      <h1>{service.name} in {location.city}, {location.state}</h1>
      <p>
        We provide professional {service.name.toLowerCase()} services in {location.city},
        serving the {location.population.toLocaleString()} residents of this thriving city.
      </p>
      <FAQSection faqs={generateFAQs(location, service)} />
    </div>
  )
}
```

## Static Generation at Build Time

generateStaticParams tells Next.js which routes to pre-render at build time. For thousands of combinations, the build takes a few minutes but produces static HTML for every page.

Static pages are served from CDN with no server computation. They load fast and rank well because Core Web Vitals are strong by default.

## Adding Unique Value Per Page

The difference between spam and useful programmatic pages is unique value. Each page should have content that could not exist without that specific combination.

Generate location-specific FAQs, embed a map for that city, list nearby areas served, and include city-specific statistics or facts. Each of these adds unique value that justifies the page's existence.

## Preventing Thin Content Penalties

Google's helpful content update targets mass-generated low-quality pages. Protect yourself by ensuring minimum content length of 300 plus words per page, unique value that answers questions only that specific combination answers, internal linking between related generated pages to build topical authority, and user-generated content like reviews or case studies for each location.

Use noindex for combinations with no search demand.

## Takeaways

- Programmatic SEO generates landing pages from data plus templates at scale
- Next.js generateStaticParams pre-renders thousands of static pages at build time
- Each page must have unique value — answer questions only that specific combination answers
- Add structured data (Service, LocalBusiness, FAQ) to every generated page
- Use noindex for combinations with no search demand to avoid thin content penalties
- Static generation means fast pages, which means good Core Web Vitals, which means better rankings
- Two days of development can generate years of organic traffic if the data and demand exist