Skip to content

Next.js Dynamic Page Param Inference — Issue & Workaround

James Cross edited this page Jun 17, 2025 · 1 revision

✅ Context

This project uses the Next.js App Router with:

  • A dynamic route at src/app/[slug]/page.tsx
  • A dynamic API route at src/app/api/location/[slug]/route.ts

The goal is:

  • /bolton → renders the dynamic page
  • /api/location/bolton → responds with API JSON for that location

⚡️ The Problem

Next.js TypeScript build (npm run build) fails with:

Type '{ params: { slug: string; }; }' does not satisfy the constraint 'PageProps'.
Type '{ slug: string; }' is missing the following properties from type 'Promise<any>': then, catch, finally, [Symbol.toStringTag]

This happens even with perfectly valid folder structure because:

  • Next.js type infers generateStaticParams vs page props.
  • If dynamic + static collide, Next sometimes treats params as Promise<any>.
  • moduleResolution: bundler in tsconfig makes it more likely.

✅ What We Tried

1️⃣ Typed generateStaticParams explicitly
2️⃣ Used { params: { slug: string } } inline
3️⃣ Used Record<string, string> for flexibility
4️⃣ Added export const dynamic = 'force-dynamic' to skip static inference
5️⃣ Verified folder structure to avoid [slug]/route.ts next to [slug]/page.tsx
6️⃣ Moved API to api/location/[slug]/route.ts to prevent conflicts
7️⃣ Cleared .next and rebuilt repeatedly

Result:
All attempts failed — the type error persisted because the build worker still sees conflicting param type signatures.


✅ Final Practical Solution

  • Removed generateStaticParams to prevent static param inference.
  • Forced the page to be dynamic:
    export const dynamic = 'force-dynamic';
  • Added a @ts-expect-error to skip type-check on the props:
    // Temporary workaround — safe to remove when Next.js fixes this bug
    // @ts-expect-error
    export default async function LocationPage(props) {
      const slug = props.params.slug;
      ...
    }

Outcome:

  • Build passes.
  • Page works for all slugs.
  • API works at /api/location/[slug].

📌 Notes

  • This is a known edge case in Next.js App Router + TS param inference.
  • Next.js core team is actively improving this.
  • Safe to run in production because runtime routing works fine.
  • Remove the @ts-expect-error once Next fixes this.

✅ References


Keep this file for future maintainers to understand why the workaround exists.