Fix
Next.js 16 params and cookies are async
Answer
In Next.js 16 params and searchParams are Promises. Always await them. cookies(), headers(), and draftMode() from next/headers are async. Await them. Synchronous access typechecks in places and crashes at runtime.
- Next.js 16.4.1
- React 19.3
- @supabase/ssr 0.12.7
- @supabase/supabase-js current, installed alongside @supabase/ssr
Last verified 2026-10-15
Agents still treat params and searchParams as plain objects, and they still call cookies(), headers(), and draftMode() without await. Synchronous access typechecks in places and crashes at runtime. The Next.js + Supabase pack is written to stop that paste. It pins Next.js 16.4.1, React 19.3, @supabase/ssr 0.12.7, and @supabase/supabase-js (current, installed alongside @supabase/ssr). Do not generate Pages Router or @supabase/auth-helpers-nextjs. When to keep middleware.ts, and when to migrate it to proxy.ts, is covered on the proxy page.
params and searchParams are Promises. Always await them. The sample page types params as Promise<{ id: string }>, awaits that promise, and then renders id. cookies(), headers(), and draftMode() from next/headers are async. Await them on every call. Server Components are the default. Add "use client" only when the file uses state, effects, browser APIs, or event handlers. Push that file to a leaf component.
New create-next-app projects enable Cache Components. Do not cache a response that depends on a cookie or a user. Authenticated reads stay dynamic. Read the cookie with await cookies() inside the component that needs it, and do not store that result on a cached response. Browser code uses createBrowserClient. Server Components, Server Actions, and Route Handlers use createServerClient with cookies.getAll and cookies.setAll.
Awaiting cookies() does not decide whether the request is allowed. To decide whether a request is allowed, call supabase.auth.getClaims(). Use getSession() only to read the raw token, and never trust session.user for authorization. The done checklist is npx tsc --noEmit and next build pass, or you said why they could not run. New tables have RLS enabled plus policies, in a migration file. No getSession() is used for authorization, and no select("*") was added.
Last verified 2026-10-15. doc-verified; TypeScript snippets pass tsc --strict (TypeScript 5, @types/react 19) with zod and the Supabase client stubbed.
Next.js + Supabase pack — Next.js 16.4 and Supabase SSR, with the APIs that actually work.
doc-verified; TypeScript snippets pass tsc --strict (TypeScript 5, @types/react 19) with zod and the Supabase client stubbed
Sample from the pack
The page sample in the Next.js + Supabase rules file.
export default async function Page({
params,
}: {
params: Promise<{ id: string }>
}) {
const { id } = await params
return <h1>{id}</h1>
}Sources in the pack
Questions
- Are params and cookies() synchronous in Next.js 16?
- In Next.js 16 params and searchParams are Promises. Always await them. cookies(), headers(), and draftMode() from next/headers are async. Await them. Synchronous access typechecks in places and crashes at runtime.
- How do I read params in Next.js 16?
- params and searchParams are Promises. Always await them. The sample page types params as Promise<{ id: string }>, awaits that promise, and then renders id. Synchronous access typechecks in places and crashes at runtime.
- Are cookies(), headers(), and draftMode() async in Next.js 16?
- Yes. cookies(), headers(), and draftMode() from next/headers are async. Await them. Do not cache a response that depends on a cookie or a user. Authenticated reads stay dynamic.
- When was this Next.js 16 params rule last checked?
- Last verified 2026-10-15. doc-verified; TypeScript snippets pass tsc --strict (TypeScript 5, @types/react 19) with zod and the Supabase client stubbed.