Building a Multi-City Production Portal with Next.js 14: Domain Registration, Monorepo Architecture, and Dynamic City Pages
What Was Done
We launched the foundational infrastructure for rickdrakeproductions.com, a multi-city production coordination platform using Next.js 14 with App Router, TypeScript, and Tailwind CSS 4. The architecture supports dynamic city-specific pages and services while maintaining a single codebase. Domain registration, monorepo workspace configuration, and initial city routing were completed and tested locally at http://localhost:3000/san-diego/.
Technical Details: Project Structure & Monorepo Setup
The project uses a pnpm workspace monorepo pattern to support future service expansion:
/Users/cb/Documents/repos/sites/rickdrakeproductions.com/
├── pnpm-workspace.yaml
├── package.json
└── apps/web/
├── next.config.ts
├── package.json
└── src/
├── app/
│ ├── globals.css
│ ├── layout.tsx
│ ├── page.tsx
│ └── [city]/
│ ├── layout.tsx
│ ├── page.tsx
│ ├── services/page.tsx
│ ├── fleet/page.tsx
│ ├── fleet/[vehicle]/page.tsx
│ ├── contact/page.tsx
│ ├── about/page.tsx
│ ├── locations/page.tsx
│ └── portfolio/page.tsx
├── components/layout/
│ ├── Nav.tsx
│ └── Footer.tsx
└── lib/
├── types.ts
├── cities.ts
└── content.ts
Why pnpm? It provides strict dependency isolation and reduced disk footprint compared to npm/yarn, critical when scaling to multiple service packages later. The workspace configuration allows each app to have independent versioning while sharing root-level tooling.
Dynamic City Routing Architecture
The core routing pattern uses Next.js dynamic segments via the [city] parameter. This enables:
- Single codebase serving multiple cities (
/san-diego/,/las-vegas/, etc.) - Shared layout and navigation components
- City-specific content loaded from
src/lib/cities.tsandsrc/lib/content.ts - SEO-friendly URL structure for search engine crawling
The src/lib/cities.ts file defines supported cities and their metadata:
export const CITIES = {
'san-diego': { name: 'San Diego', slug: 'san-diego', ... },
'las-vegas': { name: 'Las Vegas', slug: 'las-vegas', ... },
// Phoenix, Palm Springs, LA on roadmap
}
Route generation in next.config.ts uses generateStaticParams() to pre-render city pages at build time, avoiding runtime lookups and improving Time-to-First-Byte:
// In [city]/page.tsx
export async function generateStaticParams() {
return Object.keys(CITIES).map((slug) => ({
city: slug,
}));
}
Infrastructure: Domain Registration & DNS
Domain Registration Decision: We registered rickdrakeproductions.com via AWS Route53 instead of a third-party registrar. This decision was made because:
- Unified DNS management: No handoff between registrar and hosted zone operator
- API-first workflow: Future automation can programmatically manage DNS records
- Cost simplicity: $12.99/year registration + Route53 hosting (~$0.40/month)
- Privacy protection: Registered with WHOIS privacy enabled to prevent spam solicitation
Existing domain portfolio context: The account manages domains including 86from.com (Namecheap) and dangerouscentaur.com (Route53 + CloudFront). This project uses the proven Route53 pattern for consistency.
Route53 Registration Details:
- Domain:
rickdrakeproductions.com - Registrant contact: Retrieved from existing Route53 domain profiles (avoiding re-entry)
- Privacy protection: Enabled
- Nameservers: Route53 default (four NS records)
Build & Deployment Considerations
The Next.js build pipeline revealed a critical dependency issue with Tailwind CSS 4's native binaries. The lightningcss package requires platform-specific compiled binaries (e.g., lightningcss-darwin-x64 for macOS). Resolution steps:
- Installed
lightningcss-darwin-x64as an optional dependency inapps/web/node_modules/ - Verified the binary path and loader mechanism in
lightningcss/index.js - Confirmed successful build completion after binary installation
This matters for CI/CD pipelines: build environments must have appropriate OS-level native dependencies or Docker builds must target matching architectures.
SEO & Referral Business Model
The URL architecture supports two business models:
- Direct partnership: Subdomains or city pages become Rick's owned services, driving brand loyalty
- Referral capture: If Rick doesn't adopt the platform, each city page captures leads and refers them to Rick with attribution tracking (future: UTM parameters, referral codes in contact forms)
Dynamic city pages rank independently in Google for city-specific searches ("production coordinator San Diego"), while the hub domain builds authority across all markets.
Key Decisions & Trade-offs
| Decision | Alternative Considered | Rationale |
|---|---|---|
Dynamic [city] routes |
Separate subdomains (san-diego.rickdrake...) | Single domain = simpler DNS, shared analytics, better hub authority |
| Static generation + ISR | Server-side rendering | Edge caching improves TTFB; content updates via ISR revalidation |