I am building a website using Next.js 15.3.3, React 19, and Sanity.io for content. Everything works perfectly in my local development environment (next dev). However, when I deploy to Vercel, the build fails during the "Checking validity of types" step with a TypeScript error on my dynamic pages (like app/category/[slug]/page.tsx).
Error:
Failed to compile.
app/category/[slug]/page.tsx
Type error: Type 'CategoryPageProps' does not satisfy the constraint 'PageProps'.
Types of property 'params' are incompatible.
Type '{ slug: string; }' is missing the following properties from type 'Promise<any>': then, catch, finally, [Symbol.toStringTag]
Here is the code for the failing page, app/category/[slug]/page.tsx:
// app/category/[slug]/page.tsx
import { client as sanityClient } from "@/lib/sanity";
import { createClient as createServerSupabaseClient } from "@/lib/supabase/server";
import { notFound } from "next/navigation";
import ProductCard from "@/components/ProductCard";
import ProductFilters from "@/components/ProductFilters";
interface CategoryPageProps {
params: { slug: string };
searchParams: { [key: string]: string | string[] | undefined };
}
async function getData(slug: string, searchParams: CategoryPageProps['searchParams']) {
const sort = typeof searchParams.sort === 'string' ? searchParams.sort : 'latest';
const minPrice = typeof searchParams.minPrice === 'string' ? parseFloat(searchParams.minPrice) : undefined;
const maxPrice = typeof searchParams.maxPrice === 'string' ? parseFloat(searchParams.maxPrice) : undefined;
const inStock = searchParams.inStock === 'true';
// 1. Build filter and order clauses for GROQ
let orderClause = '';
if (sort === 'price-asc') orderClause = '| order(price asc)';
if (sort === 'price-desc') orderClause = '| order(price desc)';
if (sort === 'latest') orderClause = '| order(_createdAt desc)';
const filterClauses: string[] = [];
if (minPrice) filterClauses.push(`price >= ${minPrice}`);
if (maxPrice) filterClauses.push(`price <= ${maxPrice}`);
if (inStock) filterClauses.push(`stock > 0`);
const categoryQuery = `*[_type == "category" && slug.current == $slug][0]{ title, description }`;
const productsQuery = `*[_type == "product" && references(*[_type=="category" && slug.current == $slug]._id) ${filterClauses.length > 0 ? `&& ${filterClauses.join(' && ')}` : ''}] ${orderClause} {
_id, name, slug, price, compareAtPrice, images
}`;
const category = await sanityClient.fetch(categoryQuery, { slug });
const products = await sanityClient.fetch(productsQuery, { slug });
return { category, products };
}
export default async function CategoryPage({
params,
searchParams,
}: {
params: { slug: string };
searchParams: { [key: string]: string | string[] | undefined };
}) {
const { category, products } = await getData(params.slug, searchParams);
const supabase = await createServerSupabaseClient();
const { data: { user } } = await supabase.auth.getUser();
if (!category) {
return notFound();
}
return (
<>
<header className="bg-gradient-to-b from-accent/80 to-background">
<div className="max-w-4xl mx-auto text-center pt-20 pb-12 px-4">
<h1 className="text-4xl font-serif tracking-tight text-foreground sm:text-6xl">
{category.title}
</h1>
{category.description && (
<p className="mt-6 max-w-2xl mx-auto text-lg text-foreground/80">
{category.description}
</p>
)}
</div>
</header>
{/* --- MAIN CONTENT AREA --- */}
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
{/* The filter bar sits on top of the product grid */}
<ProductFilters />
<div className="mt-8">
{products.length > 0 ? (
<div className="grid grid-cols-2 sm:grid-cols-2 md:grid-cols-3 gap-4 md:gap-6">
{products.map((product: any) => (
<ProductCard key={product._id} product={product} userId={user?.id} />
))}
</div>
) : (
<div className="text-center py-16 border-2 border-dashed rounded-lg">
<p className="text-lg text-muted-foreground">No products found matching your filters.</p>
</div>
)}
</div>
</main>
</>
);
}
What I've already Tried:
What is the correct, official way to type the params prop for a dynamic Server Component in Next.js 15 to avoid this build error on Vercel?
As per the NextJs documentation both the slug
and the searchParams
are Promises
which have to be awaited.
That's also what the error is pointing out:
Type '{ slug: string; }' is missing the following properties from type 'Promise<any>': then, catch, finally, [Symbol.toStringTag]
For the solution you need to change the definition of your CategoryPageProps
to this:
interface CategoryPageProps {
params: Promise<{ slug: string }>;
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}
And then in your CategoryPage
function export you'll have to await these promises:
export default async function CategoryPage({
params,
searchParams,
}: CategoryPageProps) {
const { slug } = await params;
const resolvedSearchParams = await searchParams;
const { category, products } = await getData(slug, resolvedSearchParams);
//...
You will also want to change the signature of the getData
function to this:
async function getData(
slug: Awaited<CategoryPageProps['params']>['slug'],
searchParams: Awaited<CategoryPageProps['searchParams']>
) {
//...
If you want to learn more about types I'd suggest the typescript documentation and the typescript playground