Routing Architecture
Next.js App Router Structure
The application uses Next.js 14 App Router with a hierarchical route structure:
app/
├── (base-layout)/ # Route group (no URL segment)
│ ├── account/ # /account
│ └── page.tsx # / (root)
│
├── [primaryTeam]/ # Dynamic segment
│ ├── (team-layout)/ # Route group
│ │ ├── teams/ # /[team]/teams
│ │ ├── roles/ # /[team]/roles
│ │ └── page.tsx # /[team]
│ │
│ └── campaign/
│ └── [slug]/ # Nested dynamic segment
│ ├── dashboard/ # /[team]/campaign/[slug]/dashboard
│ └── page.tsx # /[team]/campaign/[slug]
│
└── api/ # API routes
├── essearch/ # /api/essearch
└── duplicate/ # /api/duplicate
Route Patterns
1. Static Routes
- Example:
/auth/sign-in,/auth/sign-up - Pattern: Fixed paths without dynamic segments
- Usage: Authentication pages, static content
2. Dynamic Routes
- Example:
/[primaryTeam],/[primaryTeam]/campaign/[slug] - Pattern:
[paramName]for dynamic segments - Usage: Team-scoped routes, resource-specific pages
3. Route Groups
- Example:
(base-layout),(team-layout),(data-views) - Pattern:
(groupName)- doesn't appear in URL - Usage: Organizing layouts without affecting URLs
4. Parallel Routes
- Example:
(data-views)with multiple views - Pattern: Multiple routes at the same level
- Usage: Different views of the same data (petitions, signatures, circulators)
Route Parameters Access
In Server Components:
// app/[primaryTeam]/campaign/[slug]/page.tsx
export default async function Page({
params
}: {
params: { slug: string; primaryTeam: string }
}) {
const campaign = await getCampaignBySlug(params.slug);
// ...
}
In Client Components:
'use client';
import { useParams } from 'next/navigation';
export function MyComponent() {
const params = useParams();
const teamId = params.primaryTeam;
// ...
}
Search Parameters
In Server Components:
export default async function Page({
searchParams,
}: {
searchParams: { [key: string]: string | string[] | undefined };
}) {
const step = searchParams?.step || "1";
// ...
}