skip to content

A Next.js component marked `'use client'` calls `fetch('/api/report', { next: { revalidate: 60 } })` inside a `useEffect`. Does that response end up in Next's Data Cache?

level: juniorimportance: should knowfreq 45%

answer

  1. which fetch is this, really
  2. the browser has no wrapper
  3. unknown init properties are ignored
  4. types augment globally, runtime does not

basics

~20 s

No. That call runs in the browser, where fetch is the platform's own function and the Next-specific next option is simply ignored. Next's Data Cache is a server-side store that browser requests never write to.

solid answer

~50 s

No. Next's caching options live on the `fetch` that Next patches **on the server**; a `useEffect` body runs in the browser, where `fetch` is the platform function. An unrecognised `next` property on the init object is just ignored — no error, no warning, no cache entry. The Data Cache is a server-side store, and a request the browser makes never touches it. What confuses people is that TypeScript accepts the code: Next augments the global `RequestInit` type with `next`, so the option type-checks everywhere in the project, including in client files where it does nothing at runtime. If you want that response cached by Next, move the fetch to a Server Component or a server module and pass the data down. If you want the browser to reuse it, that is a matter of the response's HTTP caching headers instead.

go deeper

for a junior

Know that Next's fetch caching options only apply to fetches that run on the server, and that code inside a useEffect always runs in the browser.

for a middle

Be ready to explain why nothing errors: the browser ignores unknown init properties, and Next's global type augmentation makes the option type-check in every file regardless of where it runs.

for a senior

Show that you would catch this in review by treating a next: option in a 'use client' file as a smell, and that you know the right fix is to move the fetch to the server and pass data down rather than reaching for a client-side cache option.

for a principal

Own the guardrail: decide how a codebase prevents server-only caching hints from drifting into client modules — lint rules, module boundaries, or a convention that data enters client components as props — instead of relying on reviewers to spot it.

## Where the option is actually read Next.js installs its own `fetch` on the server. That wrapper inspects `cache` and the Next-only `next` option, and decides whether to write the result into the **Data Cache**. In the browser there is no wrapper — `window.fetch` is the platform implementation, defined by the Fetch standard, and the standard says nothing about a `next` property. The Web platform's rule for the init object is permissive: unknown properties are ignored. So the call succeeds, the network request goes out normally, and the option has exactly zero effect. There is no runtime warning to tell you. ```ts 'use client' import { useEffect, useState } from 'react' export function Report() { const [data, setData] = useState<unknown>(null) useEffect(() => { // Runs in the browser. `next` is an unknown init property here. fetch('/api/report', { next: { revalidate: 60 } }) .then((r) => r.json()) .then(setData) }, []) return <pre>{JSON.stringify(data)}</pre> } ``` ## Why the type checker does not save you Next ships a global type augmentation that adds an optional `next` field to `RequestInit`. Type augmentation is project-wide — it cannot be scoped to "only files that run on the server," because that is a runtime property, not a type-system one. The consequence is that this mistake compiles cleanly and passes lint. It is one of the few Next caching errors with no signal at all until someone watches the network tab and sees a request on every mount. ## A subtlety about `'use client'` `'use client'` does not mean "this code only ever runs in the browser." The component function itself can still run on the server during the initial render to produce HTML. What is guaranteed is that an effect body does **not** run on the server — effects run only after the component is live in the browser. So a fetch inside `useEffect` is unambiguously a browser fetch, every time, and the reasoning above holds without exception. A fetch written at module scope in a client file, or in the component body, is a murkier case and a worse idea for other reasons; the clean answer is that Next's caching options belong to server code. ## What to do instead The choice depends on what you actually wanted. - **You wanted Next to cache the data.** Fetch it in a Server Component or a server-only module, with `cache: 'force-cache'` or `next: { revalidate: N }`, and pass the result into the client component as a prop. - **You wanted the browser to avoid refetching.** That is HTTP caching: the response's own cache headers govern it, and that behaviour is owned by the server that produced the response, not by a Next option. - **You wanted deduplication across components on the same screen.** A client-side data library or lifting the fetch to a shared parent is the tool; the Data Cache is not, because it is not reachable from the browser. ## The general rule worth remembering Every Next caching primitive that hangs off `fetch` — `cache: 'force-cache'`, `next.revalidate`, `next.tags` — is a server-side instruction. If you cannot draw a straight line from the call site to code executing on the server, the option is decoration. When reviewing a diff, seeing `next: { ... }` in a file whose first line is `'use client'` is a reliable smell worth stopping on.

  • If the option does nothing, why does TypeScript accept it in a client file?
    Because Next augments the global `RequestInit` type to include `next`, and a global augmentation applies to every file in the project. The type system has no notion of which files execute on the server, so it cannot narrow the augmentation to server code. The result is a mistake that compiles, lints, and runs without complaint — you only see it in the network tab.
  • Does the same reasoning apply to `cache: 'force-cache'` written in a client component?
    Partly. Unlike `next`, `cache` is a real Web fetch option, so the browser does read it — but it then applies it to the browser's own HTTP cache, following HTTP semantics. It does not create a Next Data Cache entry. So the option is not ignored, it simply means something different from what the author was picturing.
  • How would you get this data cached by Next while still rendering it in a client component?
    Fetch it on the server and hand it over. A Server Component does the `fetch` with `force-cache` or a `revalidate` interval, then renders the client component with the result as a prop. The client component keeps its interactivity and stops owning the request, and the response now lives in the Data Cache where later requests can reuse it.

saying these in an interview costs you the question

  • Believing next.revalidate works anywhere fetch is called
  • Assuming a compile error would catch the mistake
  • Thinking the Data Cache is reachable from the browser
  • Confusing Next's Data Cache with the browser's HTTP cache

context