cookies()
Reads incoming request cookies and writes outgoing ones from Server Components, Server Functions and Route Handlers.
function cookies(): Promise<ReadonlyRequestCookies>- Added in Next.js v13.0.0
Return value
Promise<ReadonlyRequestCookies>A promise resolving to a cookie store with get('name'), getAll(), has('name'), set(name, value, options), delete(name) and toString().
Errors
Calling .set() or .delete() while rendering a Server Component throws, because response headers can no longer be changed once rendering has begun; those methods are only valid inside a Server Function or Route Handler.
Changelog
- PHP v15.0.0-RCcookies is now an async function and must be awaited. A codemod is available; the synchronous access path still works in 15 but is deprecated.
- PHP v13.0.0cookies introduced.
cookies() is the request-scoped cookie store. Reading works anywhere on the server; writing does not. The browser owns cookie storage, and the server can only instruct it through a Set-Cookie response header — which means set() and delete() are valid only in a Server Function or a Route Handler, where a response is still being assembled.
When to reach for it
Use it for anything carried per-visitor in a cookie: a session id you exchange for a user, a theme or locale preference, a feature-flag bucket. If the value arrives in a header instead, use headers().
What trips people up
- It is async.
cookies().get('x')returns a method on a Promise and fails; you mustawaitfirst. get()returns an object, not a string. The value is on.value, and the whole result isundefinedwhen the cookie is absent.- It is a request-time API, so touching it in a layout or page opts that route into dynamic rendering. With Cache Components, calling it outside a
<Suspense>boundary stops the route being prerendered at all. delete()only works on cookies from the same domain and protocol that set them.
Example
import { cookies } from 'next/headers'
export default async function Page() {
const cookieStore = await cookies()
const theme = cookieStore.get('theme')?.value ?? 'light'
const hasSession = cookieStore.has('session-id')
return (
<main>
<p>Theme: {theme}</p>
<p>Signed in: {hasSession ? 'yes' : 'no'}</p>
</main>
)
}When to use it
- Read a session id in a Server Component and load the current user
- Set an httpOnly auth cookie from a login Server Function
- Clear a session cookie in a sign-out Route Handler
- Read a theme cookie to pick the server-rendered colour scheme
Frequently asked questions
Why can't I set a cookie in a Server Component?
Because HTTP does not allow cookies to be set once the response has started streaming, and a Server Component renders into that stream. Move the set() into a Server Function invoked from the client, or into a Route Handler, where the response headers are still open.
Why does cookies() return a Promise now?
It became async in v15.0.0-RC so Next.js can defer request access until the point it is actually needed. Await it — const store = await cookies() — or pass it to React's use(). A codemod ships with the 15 upgrade guide, and the old synchronous access is deprecated.
Does calling cookies() make my page dynamic?
Yes. It is a request-time API, so any layout or page that reads it opts into dynamic rendering. With Cache Components enabled, wrap the component that calls it in a <Suspense> boundary so the rest of the route can still be prerendered into the static shell.
More examples
Setting a cookie in a Server Function
The 'use server' directive is what makes writing legal here: the function runs during a mutation, before the response headers are flushed. The same three lines inside a page component would throw. path defaults to '/' and is the only option with a default, so every other field shown is opt-in.
'use server'
import { cookies } from 'next/headers'
export async function signIn(token: string) {
const cookieStore = await cookies()
cookieStore.set('session-id', token, {
httpOnly: true,
secure: true,
sameSite: 'lax',
path: '/',
maxAge: 60 * 60 * 24 * 7,
})
}Deleting a cookie in a Route Handler
delete() emits a Set-Cookie header that expires the cookie in the browser. It only matches a cookie set on the same domain and protocol — a cookie set over HTTPS on a parent domain will not be removed by a delete() served over HTTP, which is the usual reason a sign-out appears to do nothing.
import { cookies } from 'next/headers'
export async function POST() {
const cookieStore = await cookies()
cookieStore.delete('session-id')
return new Response(null, {
status: 303,
headers: { Location: '/' },
})
}Listing every cookie on the request
getAll() with no argument returns every cookie the browser sent, each as a { name, value } object. Passing a name instead returns only the entries matching it, which matters when a client holds several cookies under one name at different paths.
import { cookies } from 'next/headers'
export default async function DebugPage() {
const cookieStore = await cookies()
return (
<ul>
{cookieStore.getAll().map((cookie) => (
<li key={cookie.name}>
{cookie.name}: {cookie.value}
</li>
))}
</ul>
)
}
Discussion