Building a Multi-City Production Portal with Next.js 14: Domain Registration, Monorepo Setup, and Dynamic Routing
We recently began building rickdrakeproductions.com—a multi-city web platform that serves as a hub for production coordination services across San Diego, Las Vegas, and future expansion cities. This article covers the domain acquisition, monorepo architecture, Next.js 14 App Router implementation, and the infrastructure decisions that enable city-specific content at scale.
Domain Strategy and Registration
Rather than maintaining separate WordPress instances for each city (like the aging sandiegoproductions.com), we opted for a single dynamic Next.js application that uses URL-based city segmentation. This approach provides:
- SEO consolidation: One authoritative domain with city-specific pages
- Maintenance simplicity: Single codebase, single deployment pipeline
- Referral business fallback: If Rick Drake Productions doesn't adopt this as their web dev service, the platform captures leads and routes them with a referral fee structure
We registered rickdrakeproductions.com via AWS Route53 rather than GoDaddy to avoid domain registrar fragmentation. The registration included privacy protection and was validated through the existing Route53 contact information from other registered domains in the account.
Monorepo Architecture with pnpm Workspaces
The project uses a pnpm workspace to manage the web application and future services. The structure created:
rickdrakeproductions.com/
├── pnpm-workspace.yaml
├── package.json (workspace root)
└── apps/
└── web/
├── next.config.ts
├── package.json
└── src/
├── app/
├── components/
└── lib/
The pnpm-workspace.yaml declares workspaces at apps/*, and the root package.json includes shared dependencies. This structure allows us to add additional services (APIs, admin dashboards, mobile apps) without duplicating dependency management.
Next.js 14 App Router with Dynamic City Routing
The core routing strategy uses Next.js dynamic segments. City pages are generated dynamically from a city configuration file:
src/app/
├── layout.tsx (root layout)
├── page.tsx (homepage)
├── [city]/
│ ├── layout.tsx (city-specific layout wrapper)
│ ├── page.tsx (city landing page)
│ ├── services/page.tsx
│ ├── fleet/page.tsx
│ ├── fleet/[vehicle]/page.tsx
│ ├── contact/page.tsx
│ ├── about/page.tsx
│ ├── locations/page.tsx
│ └── portfolio/page.tsx
The [city] segment is a dynamic route parameter that matches against valid cities defined in src/lib/cities.ts:
// src/lib/cities.ts excerpt
export const CITIES = ['san-diego', 'las-vegas', 'phoenix', 'palm-springs', 'los-angeles'];
export type City = typeof CITIES[number];
The src/lib/types.ts file establishes TypeScript interfaces for city-specific content:
export interface CityContent {
city: City;
displayName: string;
description: string;
// ... additional fields
}
This allows src/app/[city]/page.tsx to receive the city parameter and query content from src/lib/content.ts, which returns city-specific copy, imagery, and metadata.
Component Organization and Styling
Layout components are centralized in src/components/layout/:
Nav.tsx:Navigation bar with city-aware linksFooter.tsx:Global footer with city-specific contact information
The application uses Tailwind CSS 4 with the default Next.js configuration. The global stylesheet at src/app/globals.css imports Tailwind directives and custom CSS variables for consistent theming across cities.
Critical dependency: During the build process, we encountered a native binary compilation issue with lightningcss, which Tailwind CSS 4 uses for CSS parsing. This required explicitly installing the lightningcss-darwin-x64 platform-specific binary. The build command in next.config.ts uses the system's native CSS compiler rather than falling back to JavaScript implementations, improving build performance.
Next.js Build Configuration
The next.config.ts file includes:
- TypeScript strict mode enabled
- Image optimization with next/image for responsive imagery
- Environment variable validation for city list
- CSS handling configured for Tailwind CSS 4 and lightningcss
We verified the build process completes without errors before deployment infrastructure changes.
Infrastructure: DNS and CDN Considerations
The application will be deployed to Vercel (which provides automatic CDN distribution). However, if self-hosting becomes necessary, the following infrastructure is available:
- DNS: AWS Route53 hosts rickdrakeproductions.com and routes traffic to either Vercel or a custom origin
- Existing CloudFront distribution: A distribution (ID: E2Q4UU71SRNTMB) exists for related properties and can be extended to serve rickdrakeproductions.com
- SSL/TLS: ACM certificates are managed for wildcard and subdomain coverage
The current deployment approach is Vercel-first because it integrates seamlessly with Next.js 14, provides automatic image optimization, and eliminates the need to manage CloudFront origin configurations manually.
Content Management and Scalability
The src/lib/content.ts file currently returns hardcoded city content. This is acceptable for the MVP, but the architecture allows for future upgrades:
- Static generation: Use
generateStaticParams()in[city]/page.tsxto pre-render all city pages at build time - CMS integration: Replace hardcoded content with calls to Contentful, Sanity, or Strapi
- Database-driven: Move city configurations and service listings to a PostgreSQL or DynamoDB backend
For now, the static content approach minimizes infrastructure complexity and leverages Next.js's build-time optimizations.
Key Decisions and Rationale
- Single domain with dynamic routes vs. subdomains: Dynamic routes consolidate SEO authority, simplify SSL/TLS management, and reduce DNS configuration complexity. Subdomains would require multiple wildcard certificates and separate analytics tracking.
- pnpm workspaces over Lerna/Nx: pnpm provides faster dependency resolution and automatic workspace linking without external configuration.
- App Router over Pages Router: Server components enable better performance (zero client JavaScript for static content), built-in layouts, and cleaner async/await patterns in page components.
- Vercel deployment