Skip to the solution
MediumFrontend9 min read

How do you read cookies and headers in Next.js (cookies(), headers())?

1.0k views

The problem

Question presented to candidate: "A Server Component needs to know the user's theme cookie and preferred language from the request headers. Show how you read them in the App Router, then explain how you would let the user change the theme, and what reading them does to how the page is rendered."

What a strong answer should cover:

  • cookies() and headers() are imported from next/headers, are async (await them; sync access was removed in Next.js 16) and work in Server Components, Server Actions and Route Handlers β€” not in Client Components.
  • cookies() returns a store with get (an object with name and value), getAll, has, and set/delete; headers() returns a read-only Web Headers object.
  • Cookies can only be set or deleted in a Server Action or Route Handler, because the response headers are already sent once rendering streams; headers() can never be modified.
  • They are request-time APIs: using them makes the route dynamic, and with Cache Components they must sit inside <Suspense> or the build fails.
  • Security: set auth cookies with httpOnly, secure, sameSite; never trust header values blindly.

Clarifying questions expected:

  • "Does the rest of the page have to be per-user, or only this part?" β€” decides where the Suspense boundary goes.
  • "Does client JavaScript need to read the cookie?" β€” if not, make it httpOnly.

Code / implementation expected: Yes β€” reading in a Server Component and setting in a Server Action.

  • nextjs
  • cookies
  • headers
  • dynamic-rendering

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

9 min read Β· 10 sections

Read the code

Reading cookies and headers in a Server Component
// app/account/page.tsx
import { Suspense } from "react";
import { cookies, headers } from "next/headers";

async function Greeting() {
  const cookieStore = await cookies();
  const headerList = await headers();

  const theme = cookieStore.get("theme")?.value ?? "light"; // { name, value } | undefined
  const loggedIn = cookieStore.has("session");
  const language = headerList.get("accept-language")?.split(",")[0] ?? "en";

  return (
    <p>
      {loggedIn ? "Welcome back" : "Hello"} ({language}, {theme} theme)
    </p>
  );
}

export default function AccountPage() {
  return (
    <main>
      <h1>Your account</h1>
      {/* Request data inside Suspense: the heading can still be prerendered (required with Cache Components) */}
      <Suspense fallback={<p>Loading...</p>}>
        <Greeting />
      </Suspense>
    </main>
  );
}
Setting and deleting cookies in a Server Action and a Route Handler
// app/account/actions.ts β€” Server Action: may set and delete cookies
"use server";
import { cookies } from "next/headers";

export async function setTheme(formData: FormData) {
  const theme = formData.get("theme") === "dark" ? "dark" : "light";
  const cookieStore = await cookies();
  cookieStore.set("theme", theme, {
    httpOnly: true,
    secure: true,
    sameSite: "lax",
    path: "/",
    maxAge: 60 * 60 * 24 * 365,
  });
}

export async function logout() {
  (await cookies()).delete("session");
}

// app/api/theme/route.ts β€” Route Handler: read and write
import { cookies } from "next/headers";
import type { NextRequest } from "next/server";

export async function GET(request: NextRequest) {
  // Two equivalent ways to read the cookie here
  const fromHelper = (await cookies()).get("theme")?.value ?? null;
  const fromRequest = request.cookies.get("theme")?.value ?? null;
  return Response.json({ fromHelper, fromRequest });
}

export async function POST() {
  (await cookies()).set("theme", "dark", { httpOnly: true, path: "/" });
  return Response.json({ ok: true });
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 30 of 129 in Next.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Next.js questions
Up nextMedium

Middleware in Next 15 β€” what breaks from the 14 upgrade?

Continue