The release of Nextjs 15 this past October brought with it some great out-of-the-box features, support for React 19, and more. That said, there was a subtle change when it comes to handling the params object returned by the app-router specific generateStaticParams
function -- it is now expected to be wrapped with a promise and awaited before accessing whatever the particular slug or id or params-bound-identifier your dynamic route happens to be using.
Since the app router was first introduced in version 13 the params object has been synchronously resolved--until now, that is. With Next.js 15 the params object must be awaited as follows
const paramsBoundIdentifier = await params;
I decided to write a helper type to simplify the TypeScript usage around this change because I myself am guilty of forgetting that the params object is now asynchronously resolved and have recurrently run into errors/warnings related to this shift.
Type Definitions
Without further ado, here is the helper type and its type workup as a whole
export type InferGSPRTWorkup<T> =
T extends Promise<readonly (infer U)[] | (infer U)[]> ? U : T;
export type InferGSPRT<T extends (...args: any) => any> = {
params: Promise<InferGSPRTWorkup<ReturnType<T>>>;
};
Let's break down these two types
InferGSPRTWorkup<T>
export type InferGSPRTWorkup<T> =
T extends Promise<readonly (infer U)[] | (infer U)[]> ? U : T;
This type is a workup type that plays the role of internally inferring the Promise wrapped return type of the generateStaticParams
function. Additionally, it unenumerates the return type since with dynamic routes there will always be at least an array of objects being returned (sometimes it's an array of arrays if it's a catch-all or optional-catch-all-route).
This workup type wraps ReturnType<T>
within the type we'll use in the dynamic page files directly (where T
in ReturnType<T>
equals typeof generateStaticParams
).
InferGSPRT
As mentioned in the previous section, the workup type InferGSPRTWorkup
internally wraps ReturnType<T>
where T
equals typeof generateStaticParams
.
InferGSPRTWorkup<ReturnType<T>>
These are the inner-workings of the InferGSPRT
type which effectively provides us with an array of objects containing the object-bound-key-identifier (whatever the bracketed object-bound-key-identifier might be -- [slug], [id], [etc]).
Starting with Nextjs 15 on, it is necessary to wrap the internal workings depicted above with an outer Promise type. The reason to do so is because Nextjs 15 on expects the params object to be asynchronously resolved. For Nextjs versions 13 & 14 simply omit the outer Promise
wrapper from the type definition to synchronously resolve the params object.
To reiterate, the type defined below is correctly written for Next.js version 15 or later. For Next.js versions 13 or 14, omit the outer Promise type from the definition
export type InferGSPRT<T extends (...args: any) => any> = {
params: Promise<InferGSPRTWorkup<ReturnType<T>>>;
};
Usage
Using this type is as simple as
({ params }: InferGSPRT<typeof generateStaticParams>)
where the params object belongs to an export default async function _function-name_
function in your dynamic page.tsx
file. The object-bound-key-identifier can be extracted via destructuring while resolving the promise wrapped params object as follows:
const { slug } = await params;
Summary
Using this type is as simple as copy-pasting the two types defined in the Type Definitions
section of this post. Then, import the InferGSPRT
type into your dynamic page files and follow the example in the Usage
section for implementation. Please feel free to drop any questions or comments below!
Top comments (0)