← Back to Playbook
Technical GrowthMarch 12, 2026
Open Sourcing Our Programmatic SEO Next.js Stack
Want to build 10,000 landing pages in an afternoon? Here is the exact getStaticPaths code we use to dominate "city + service" keywords.
We wrote about Programmatic SEO conceptually. Today, we give you the code. This stack generates 10,000+ pages statically at build time using Next.js.
The Architecture
You need a CSV file with: City, Population, Keyword, and specific local data points (e.g., "Landmark", "Nearby Zip Codes").
// /lib/locations.ts
import fs from 'fs';
import path from 'path';
import Papa from 'papaparse';
export const getLocations = () => {
const csvFile = fs.readFileSync(path.join(process.cwd(), 'data/cities.csv'), 'utf8');
const { data } = Papa.parse(csvFile, { header: true });
return data; // Returns [{ city: "Austin", state: "TX", landmark: "The Capitol" }, ...]
};
// /app/locations/[city]/page.tsx
export async function getStaticPaths() {
const cities = getLocations();
return {
paths: cities.map((city) => ({
params: {
slug: `best-ai-agency-in-${city.slug}`
},
})),
fallback: false,
};
}
export default function LocationPage({ params }) {
const city = getCityData(params.slug);
return (
<div>
<h1>Best AI Agency in {city.name}</h1>
<p>If you are looking for AI services near {city.landmark}, we can help.</p>
<div className="map-embed">
<iframe src={`https://google.com/maps?q=${city.lat},${city.lng}`} />
</div>
</div>
);
}
Avoiding the "Doorway Page" Penalty
Google hates pages that are just "Find Plumber in [City]" with the exact same content. You must inject Local Uniqueness.
- Local Landmarks: Mention specific places ("Near the Golden Gate Bridge").
- Local Statistics: "Serving the 800,000 residents of San Francisco."
- Unique Images: Use the Google Places API to pull a photo of the specific city, or generate one with Midjourney using "[City Name] Skyline" as the prompt.
The Golden Rule: If a human user lands on the page and feels it was made for them, you win. If they feel it's a template, you bounce.