Teach async request APIs in Next.js 16 - params, searchParams, cookies(), headers(), draftMode() are now async. Use when migrating from Next.js 15, fixing type errors, or working with request data.
Handles Next.js 16 async request APIs by adding await to params, searchParams, cookies(), headers(), and draftMode() calls. Use when migrating from Next.js 15 or fixing TypeScript Promise type errors in Server Components and API routes.
/plugin marketplace add djankies/claude-configs/plugin install nextjs-16@claude-configsThis skill is limited to using the following tools:
references/async-patterns.mdTeach the breaking changes in Next.js 16 where request APIs are now async. This affects params, searchParams, cookies(), headers(), and draftMode() - all now return Promises and require await.
Route Parameters (params)
params prop is now a Promiseparams prop is now a Promiseparams argument is now a PromiseSearch Parameters (searchParams)
searchParams prop is now a PromiseRequest APIs
cookies() returns Promiseheaders() returns PromisedraftMode() returns Promiseimport { cookies, headers, draftMode } from 'next/headers';
type CookiesReturn = Promise<ReadonlyRequestCookies>;
type HeadersReturn = Promise<ReadonlyHeaders>;
type DraftModeReturn = Promise<{ isEnabled: boolean }>;
Before (Next.js 15):
export default function Page({ params }: { params: { id: string } }) {
return <div>User ID: {params.id}</div>;
}
export async function generateMetadata({ params }: { params: { id: string } }) {
return { title: `User ${params.id}` };
}
After (Next.js 16):
export default async function Page({
params
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params;
return <div>User ID: {id}</div>;
}
export async function generateMetadata({
params
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params;
return { title: `User ${id}` };
}
Before (Next.js 15):
export default function SearchPage({
searchParams
}: {
searchParams: { q?: string; page?: string }
}) {
const query = searchParams.q || '';
const page = Number(searchParams.page) || 1;
return <SearchResults query={query} page={page} />;
}
After (Next.js 16):
export default async function SearchPage({
searchParams
}: {
searchParams: Promise<{ q?: string; page?: string }>
}) {
const params = await searchParams;
const query = params.q || '';
const page = Number(params.page) || 1;
return <SearchResults query={query} page={page} />;
}
Before (Next.js 15):
import { cookies } from 'next/headers';
export default function Component() {
const cookieStore = cookies();
const token = cookieStore.get('token');
return <div>Token: {token?.value}</div>;
}
After (Next.js 16):
import { cookies } from 'next/headers';
export default async function Component() {
const cookieStore = await cookies();
const token = cookieStore.get('token');
return <div>Token: {token?.value}</div>;
}
Before (Next.js 15):
import { headers } from 'next/headers';
export default function Component() {
const headersList = headers();
const userAgent = headersList.get('user-agent');
return <div>User Agent: {userAgent}</div>;
}
After (Next.js 16):
import { headers } from 'next/headers';
export default async function Component() {
const headersList = await headers();
const userAgent = headersList.get('user-agent');
return <div>User Agent: {userAgent}</div>;
}
Before (Next.js 15):
import { draftMode } from 'next/headers';
export default function Component() {
const { isEnabled } = draftMode();
return <div>Draft mode: {isEnabled ? 'on' : 'off'}</div>;
}
After (Next.js 16):
import { draftMode } from 'next/headers';
export default async function Component() {
const { isEnabled } = await draftMode();
return <div>Draft mode: {isEnabled ? 'on' : 'off'}</div>;
}
Before (Next.js 15):
export async function GET(
request: Request,
{ params }: { params: { id: string } }
) {
const headersList = headers();
const auth = headersList.get('authorization');
return Response.json({ id: params.id, auth });
}
After (Next.js 16):
export async function GET(
request: Request,
{ params }: { params: Promise<{ id: string }> }
) {
const [{ id }, headersList] = await Promise.all([
params,
headers()
]);
const auth = headersList.get('authorization');
return Response.json({ id, auth });
}
Before (Next.js 15):
export default function Layout({
children,
params
}: {
children: React.ReactNode;
params: { locale: string };
}) {
return (
<html lang={params.locale}>
<body>{children}</body>
</html>
);
}
After (Next.js 16):
export default async function Layout({
children,
params
}: {
children: React.ReactNode;
params: Promise<{ locale: string }>;
}) {
const { locale } = await params;
return (
<html lang={locale}>
<body>{children}</body>
</html>
);
}
const { id } = params;
Fix:
const { id } = await params;
export default function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
}
Fix:
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
}
export default async function Page({ params }: { params: { id: string } }) {
const { id } = await params;
}
Fix:
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
}
const cookieStore = await cookies();
const allCookies = cookieStore.getAll();
Fix (Same, but ensure await on cookies()):
const cookieStore = await cookies();
const allCookies = cookieStore.getAll();
Inefficient (Sequential):
const { id } = await params;
const search = await searchParams;
const cookieStore = await cookies();
const headersList = await headers();
Optimized (Parallel):
const [{ id }, search, cookieStore, headersList] = await Promise.all([
params,
searchParams,
cookies(),
headers()
]);
Sequential (dependencies exist):
const { id } = await params;
const data = await fetchData(id);
Parallel (no dependencies):
const [{ id }, cookieStore] = await Promise.all([
params,
cookies()
]);
type PageParams<T = {}> = Promise<T>;
type SearchParams = Promise<{ [key: string]: string | string[] | undefined }>;
type PageProps<T = {}> = {
params: PageParams<T>;
searchParams: SearchParams;
};
export default async function Page({ params, searchParams }: PageProps<{ id: string }>) {
const { id } = await params;
const search = await searchParams;
return <div>{id}</div>;
}
For advanced Promise type handling and type guards, see @typescript/TYPES-type-guards skill.
function isValidId(id: string): id is string {
return /^[a-z0-9]+$/i.test(id);
}
export default async function Page({
params
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params;
if (!isValidId(id)) {
return <div>Invalid ID</div>;
}
return <div>Valid ID: {id}</div>;
}
export default async function Page({
params
}: {
params: Promise<{ category: string; product: string }>
}) {
const { category, product } = await params;
return (
<div>
<h1>Category: {category}</h1>
<h2>Product: {product}</h2>
</div>
);
}
export default async function Page({
params
}: {
params: Promise<{ slug: string[] }>
}) {
const { slug } = await params;
const path = slug.join('/');
return <div>Path: {path}</div>;
}
export default async function Page({
params
}: {
params: Promise<{ slug?: string[] }>
}) {
const { slug } = await params;
if (!slug) {
return <div>Home Page</div>;
}
return <div>Path: {slug.join('/')}</div>;
}
For detailed migration examples, edge cases, and troubleshooting, see:
@typescript/TYPES-type-guards for Promise type handlingWhen migrating to async request APIs:
params props to Promise<T> typesearchParams props to Promise<T> typeawait to all cookies() callsawait to all headers() callsawait to all draftMode() calls