How We Built 300+ SEO Pages from 4 JSON Files — The 0nmcp.com Content Engine

# How We Built 300+ SEO Pages from 4 JSON Files — The 0nmcp.com Content Engine 0nmcp.com has over 300 indexable pages. We don't use a CMS. We don't use a headless API. We don't use WordPress, Ghost, Contentful, or Sanity for content. We use four JSON files and Next.js dynamic routes. Here's exactly how — and why this approach outperforms traditional CMS-backed SEO. --- ## The Architecture Four data files in `src/data/` power the entire programmatic SEO engine: | File | Size | Items | Pages Generated | |------|------|-------|-----------------| | `services.json` | 50KB | 33 services | 33 integration pages + 33 Turn It 0n hubs | | `capabilities.json` | 62KB | 80 capabilities | 80+ capability deep-dive pages | | `glossary.json` | 33KB | 80 terms | 80 glossary definition pages | | `comparisons.json` | 33KB | 12 competitors | 12 comparison landing pages | Plus the community system adds: - Forum thread pages (server-rendered, unlimited) - User profile pages - Group landing pages - Blog posts Total: **300+ unique, indexable, structured-data-rich pages** from a Next.js app with zero external content dependencies. --- ## How Dynamic Routes Work Next.js App Router's `[slug]` pattern combined with `generateStaticParams()` is the foundation. Each programmatic section follows the same pattern: ```typescript // src/app/integrations/[slug]/page.tsx import servicesData from '@/data/services.json' export function generateStaticParams() { return servicesData.services.map(s => ({ slug: s.slug })) } export async function generateMetadata({ params }) { const { slug } = await params const service = servicesData.services.find(s => s.slug === slug) return { title: `${service.name} Integration — 0nMCP`, description: service.seo_description, openGraph: { ... }, alternates: { canonical: `https://0nmcp.com/integrations/${slug}` }, } } ``` `generateStaticParams()` tells Next.js to pre-render every slug at build time. Each page gets its own URL, its own metadata, its own Open Graph tags, and its own JSON-LD structured data. --- ## The JSON-LD Strategy Every page type gets the most specific [Schema.org](https://schema.org) type available: | Page Type | JSON-LD Type | Why | |-----------|-------------|-----| | [Integration pages](/integrations) | `HowTo` + `FAQPage` | Google shows rich snippets for how-to content | | [Glossary terms](/glossary) | `DefinedTerm` | Feeds Google's knowledge panels | | [Comparison pages](/compare) | `Product` + `FAQPage` | Enables product comparison rich results | | [Forum threads](/forum) | `DiscussionForumPosting` | Forum-specific rich results in search | | [User profiles](/forum) | `Person` | People-based rich results | | [Turn It 0n hubs](/turn-it-on) | `HowTo` + `FAQPage` | Step-by-step integration guides | | All pages | `BreadcrumbList` | Breadcrumb trail in search results | The JSON-LD is embedded directly in the page as a `