When I upgraded my project to Next.js 15, I expected things to just work. Instead, I was greeted with one of the most confusing TypeScript errors I’ve seen so far especially when I tried to deploy to Vercel.
Type error: Type 'ArticlePageProps' does not satisfy the constraint 'PageProps'.
Types of property 'params' are incompatible.
Type 'Params' is missing the following properties from type 'Promise<any>': then, catch, finally…
At first, I thought I did something terribly wrong. Turns out, it wasn’t just me. This issue is super common when using dynamic routes like:
src/app/article/[slug]/page.tsx
The Mistake I Made
I kept seeing examples online like this:
interface PageProps {
params: Promise<{ slug: string }>
}
export default async function Page({ params }: PageProps) {
const { slug } = await params
return <div>{slug}</div>
}
It looked correct. It worked locally.
But Vercel absolutely refused to build it.
Why This Error Actually Happens
Here’s what I eventually learned:
Where You Access params | Is It a Promise? |
---|---|
Inside generateMetadata or generateStaticParams | Yes |
Inside regular Page components (page.tsx ) | No |
So when I typed params
as Promise<...>
inside a Page component, I was lying to TypeScript and it yelled at me like a strict school teacher.
The Correct Way to Type params
in Next.js 15 Pages
Here’s the working solution:
interface PageProps {
params: {
slug: string
}
}
export default function Page({ params }: PageProps) {
const { slug } = params
return <div>Article: {slug}</div>
}
No Promise
. No await
. Just a plain object with the slug
.
Making It More Beginner-Friendly
Since I’m building a support article system, I didn’t want the page to crash if someone entered an invalid slug. So I added a simple fallback:
interface PageProps {
params: {
slug?: string
}
}
export default function Page({ params }: PageProps) {
const slug = params?.slug
if (!slug) {
return <div>No article found. Please check your URL.</div>
}
return (
<main>
<h1>Article: {slug}</h1>
<p>Coming soon! You can render article content here based on the slug.</p>
</main>
)
}
Now my page won’t break even if someone manually changes the URL.
Bonus Practice Idea
If you want to level it up, try connecting the slug to real data, like:
const articles = {
"getting-started": "Welcome to our platform! Here's how to get started...",
"billing": "Here's how billing works in our system...",
}
export default function Page({ params }: PageProps) {
const content = articles[params.slug || ""]
if (!content) return <div>404 — Article not found.</div>
return (
<main>
<h1>{params.slug}</h1>
<p>{content}</p>
</main>
)
}
Final Thought
Running into strange TypeScript errors like this can be frustrating especially when you feel like you did everything “correctly.” But this experience reminded me that most bugs aren’t failures they’re just missing context. Once I understood when params
should be a Promise and when it shouldn’t, the error instantly made sense. If you’re new to Next.js or still finding your footing like I am, don’t get discouraged every problem you solve like this turns confusion into confidence. Keep going. You’re learning faster than you think.