October 30, 2024
O. Wolfson
Next.js 15 introduces several enhancements to improve server-side rendering, parallel data fetching, and integration with React 19. One key change is that dynamic parameters like searchParams
and params
are now treated as Promises. This adjustment is part of a broader push in Next.js 15 to optimize server performance by making server-side data fetching fully asynchronous and parallel.
In this guide, we’ll focus on how to handle the new Promise-based searchParams
type, with a brief overview of the other major changes in Next.js 15.
The shift to Promise-based searchParams
and params
allows for:
This upgrade improves performance and simplifies handling of dynamic data in Next.js applications.
searchParams
Changes from Next.js 14 to Next.js 15In Next.js 14, searchParams
and params
were synchronously available in server components. This made them easy to access directly:
typescript// Next.js 14: Direct synchronous access to `searchParams`
export default function Page({
searchParams,
}: {
searchParams: { [key: string]: string | string[] | undefined };
}) {
return <div>Query: {searchParams.query}</div>;
}
With Next.js 15, searchParams
is now a Promise
, meaning it must be awaited in server components. This allows Next.js to fetch these parameters in parallel with other data, optimizing performance.
typescript// Next.js 15: `searchParams` as a Promise
export default async function Page({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}) {
const resolvedParams = await searchParams;
return <div>Query: {resolvedParams.query}</div>;
}
With this change, you’ll need to ensure that all components and functions accessing searchParams
are handling it asynchronously, awaiting it where necessary.
searchParams
In a typical page component, you would access searchParams
by awaiting it before use. Here’s how it looks:
typescript// Next.js 15: Await `searchParams` in page components
export default async function Page({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}) {
const resolvedParams = await searchParams;
return <div>Query: {resolvedParams.query}</div>;
}
searchParams
With async searchParams
, you can fetch multiple data sources concurrently, which is especially useful in data-intensive applications:
typescriptexport default async function Page({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}) {
// Fetch searchParams and API data concurrently
const [resolvedParams, userData] = await Promise.all([
searchParams,
fetch("/api/user").then((res) => res.json()),
]);
return (
<div>
<div>Query: {resolvedParams.query}</div>
<div>User: {userData.name}</div>
</div>
);
}
This approach optimizes performance, reducing server response time by handling all async operations in parallel.
searchParams
to Client ComponentsWhen using searchParams
with client components, it’s best to resolve it in the server component first. Then, pass the resolved value down as a prop, avoiding unnecessary Promise handling in the client component.
Server Component Example:
typescript// Server component: Resolve `searchParams` and pass down
export default async function Page({
searchParams,
}: {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}) {
const resolvedParams = await searchParams;
return <ClientComponent params={resolvedParams} />;
}
Client Component Example:
typescript// Client component: Directly use resolved params
"use client";
export default function ClientComponent({
params,
}: {
params: { [key: string]: string | string[] | undefined };
}) {
return <div>Query: {params.query}</div>;
}
Along with the async handling of searchParams
, Next.js 15 introduces other notable updates:
useActionState
and use
for handling promises.cookies
, headers
, and draftMode
are now async, making data access more flexible and performant.For more details, see the Next.js 15 documentation.