ctan-dev/nextjsdynamicroutes icon
public
Published on 5/22/2025
Next.js 15+ Dynamic Routes Rules

Rules

CRITICAL: In Next.js 15+, the params prop is a Promise and MUST be handled asynchronously. Follow these rules:

  1. ALWAYS type params as Promise:

    • Single param: Promise<{ slug: string }>
    • Multiple params: Promise<{ categoryId: string; itemId: string }>
    • Catch-all: Promise<{ slug: string[] }>
    • Optional catch-all: Promise<{ slug?: string[] }>
  2. ALWAYS use async/await pattern:

    • Make component function async: export default async function MyPage({ params })
    • Await params before accessing: const { slug } = await params;
    • NEVER access params directly: params.slug is INCORRECT
  3. Apply to ALL dynamic route files:

    • Pages: app/[slug]/page.tsx
    • Layouts: app/[slug]/layout.tsx
    • Route handlers: app/api/[id]/route.ts
    • All HTTP methods (GET, POST, PUT, DELETE, etc.)
  4. Alternative: Use React's use() hook:

    • Import: import { use } from 'react';
    • Usage: const { slug } = use(params);
  5. Proper error handling:

    • Wrap in try/catch: try { const { slug } = await params; } catch { return <ErrorPage />; }
  6. generateStaticParams should return objects matching param structure:

    • [slug] → [{ slug: 'value' }]
    • [categoryId]/[itemId] → [{ categoryId: 'cat', itemId: 'item' }]
  7. FORBIDDEN patterns (will cause errors):

    • const { slug } = params; // Synchronous access
    • params.slug // Direct property access
    • Non-async functions with param access

Remember: Synchronous access is deprecated in Next.js 15+ and will be removed.