The pattern: one route, data-many pages
You don't hand-write hundreds of files. You write one dynamic route and one data array; generateStaticParams turns each entry into a statically-generated page at build time. Add an entry to the array and you get the page, its metadata, its structured data, its OG image and its sitemap entry — all derived.
// app/hire/[slug]/page.js
export const dynamicParams = false; // only known slugs; others 404
export function generateStaticParams() {
return HIRE.map((h) => ({ slug: h.slug }));
}
export async function generateMetadata({ params }) {
const h = hireBySlug((await params).slug);
return { title: h.title, alternates: { canonical: `${SITE}/hire/${h.slug}` } };
}The line between programmatic and spam
Google's scaled-content-abuse policy doesn't punish programmatic pages — it punishes thin, duplicative, low-value ones. The difference is entirely in the data: each page must answer a real query with genuinely different, useful content. A template filled with the same paragraph and a swapped keyword is spam; a template filled with real, distinct substance per entry is a legitimate landing-page system.
The template is fine. The content is what's judged. If two of your generated pages are 90% identical, you have thin content no matter how clean the code is.
Make each page genuinely distinct
- Unique lead, real specifics, and page-relevant examples per entry — not a keyword swap.
- Page-appropriate structured data (Service/FAQPage/BreadcrumbList) generated from the entry.
- Per-page OG images generated from the data (Next.js opengraph-image + ImageResponse).
- A canonical per page and no two pages competing for the exact same query.
Wire them so they're not orphaned
Generation isn't enough — the pages need to be reachable. A hub/index page maps over the array and links every entry; the sitemap spreads the array automatically; related pages cross-link. This is what turns a pile of generated URLs into an internally-linked cluster that actually accrues authority.
Key takeaways
- One dynamic route + generateStaticParams + a data array = many static pages, fully derived.
- Programmatic isn't penalized; thin/duplicative content is — the substance lives in the data.
- Generate distinct metadata, JSON-LD and OG images per entry.
- Add a hub page + sitemap spread + cross-links so nothing is orphaned.