# Server State & Data Fetching > Async, shared, remote-owned data that requires a dedicated cache layer. Frontend architecture guidance from fearchitect, written by Abas Turabli and last reviewed 2026-06-21. Source: https://fearchitect.com/topics/server-state-data-fetching Use it as reference for the task at hand. Before changing code, check this guidance against the codebase: where the code already makes a different, deliberate choice, flag the conflict instead of rewriting it. Library APIs move faster than this guide, so confirm exact signatures in the official docs linked at the end. ## Summary Server state — API responses, database records — is async, shared across components, and has an expiry. TanStack Query and SWR manage it with a client-side cache keyed by query keys, background revalidation, and request deduplication. React Server Components shift some of this work to the server entirely, removing the need for a client cache layer in those cases. ## Definition Server state differs from client state (UI toggles, form fields) in four ways: it's **async** (requires a network round-trip), **shared** (multiple components observe the same data), **remote-owned** (the server can change it without the client knowing), and **potentially stale** (a cached copy drifts from the truth over time). TanStack Query v5 and SWR give each piece of server state a **query key** — a serialisable identifier — and store the response in an in-process cache. They track loading/error/success so components don't have to, and revalidate in the background automatically. ## Server state vs client state - **Async** — server state always involves a network fetch; client state (modals, form values) is synchronous. - **Shared** — multiple components read the same cache slot; changing it anywhere propagates everywhere. - **Remote-owned** — the server mutates it independently; the client only holds a snapshot. - **Stale-capable** — cached data drifts without explicit revalidation; `staleTime` controls the freshness window. - **RSC alternative** — React Server Components fetch on the server and ship HTML; no client cache needed for that data. ## TanStack Query v5 — useQuery (object syntax) v5 dropped the positional overload. Every call takes a single options object. `queryKey` is the cache identity; `staleTime` sets the freshness window in ms. **useQuery with staleTime (TanStack Query v5)** ```tsx import { useQuery } from "@tanstack/react-query"; interface Post { id: number; title: string; } async function fetchPost(id: number): Promise { const res = await fetch(`/api/posts/${id}`); if (!res.ok) throw new Error("fetch failed"); return res.json(); } export function PostDetail({ id }: { id: number }) { const { data, isPending, isError } = useQuery({ queryKey: ["post", id], // cache key — changes when id changes queryFn: () => fetchPost(id), staleTime: 30_000, // fresh for 30 s; no background refetch while fresh }); if (isPending) return

Loading…

; if (isError) return

Error loading post.

; return

{data.title}

; } ``` Object-syntax `useQuery` (v5 required). `staleTime: 30_000` prevents redundant refetches for 30 seconds. Changing `id` creates a new cache entry automatically. ## Diagram ```mermaid sequenceDiagram participant C1 as Component A participant C2 as Component B participant TQ as TanStack Query Cache participant API as API Server C1->>TQ: useQuery({queryKey:['user',1]}) TQ->>API: fetch /users/1 (cache miss) API-->>TQ: {name:"Alice"} TQ-->>C1: data (cached, fresh) C2->>TQ: useQuery({queryKey:['user',1]}) TQ-->>C2: data (deduped, no network call) Note over TQ: staleTime expires C1->>TQ: window focus TQ->>API: background refetch API-->>TQ: updated data TQ-->>C1: data updated silently TQ-->>C2: data updated silently ``` Two components share one cache slot; after staleTime expires, focus triggers a silent background refetch that updates both. ## Watch out: RSC fetch is not the same as client-cache fetch In Next.js App Router, identical `fetch` calls within one render are deduped (request memoization, on by default). The Data Cache — persisting results across requests — is off by default; opt in with `{ next: { revalidate } }` or `{ cache: 'force-cache' }`. Fetching the same resource in both an RSC and a client `useQuery` without coordination sends duplicate requests. ## Key terms - **query key**: Serialisable array that identifies a cache entry in TanStack Query or SWR; changing it triggers a new fetch. - **staleTime**: Duration in ms during which cached data is considered fresh and no background refetch fires. - **gcTime**: How long an unused TanStack Query cache entry is kept in memory before garbage collection. Default 5 min. - **background revalidation**: Refetch triggered silently on focus or reconnect; updates the cache without a loading spinner. - **request deduplication**: Multiple components calling `useQuery` with the same key share one in-flight network request. ## Related topics - [Client State Management](https://fearchitect.com/topics/client-state-management.md): Decide where UI state lives; pick the right tool for the scope. - [Optimistic UI & Mutations](https://fearchitect.com/topics/optimistic-ui-mutations.md): Update the UI before the server replies; roll back on error. - [Real-time: WebSockets vs SSE vs Polling](https://fearchitect.com/topics/realtime-websockets-sse-polling.md): Match the right real-time transport to your data-flow direction. - [Backend for Frontend (BFF)](https://fearchitect.com/topics/backend-for-frontend.md): A per-client server layer that shapes and aggregates APIs for one frontend. - [REST vs GraphQL vs tRPC](https://fearchitect.com/topics/rest-vs-graphql-vs-trpc.md): Three API styles with distinct fetch, type, and caching trade-offs. - [Autocomplete / Typeahead](https://fearchitect.com/topics/autocomplete-typeahead.md): Debounced input, AbortController cancellation, and ARIA combobox. ## Further reading - [TanStack Query v5 — Overview](https://tanstack.com/query/latest/docs/framework/react/overview) - [TanStack Query — Important Defaults](https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults) - [SWR — Getting Started](https://swr.vercel.app/docs/getting-started) - [Next.js — Data Fetching (App Router)](https://nextjs.org/docs/app/building-your-application/data-fetching)