Client State vs Server State
Part of the Full Stack Developer Roadmap
In short
Is the modal open? That's yours. What's in the user's saved cart? That belongs to the backend, and your copy can go stale. Mixing the two causes many 'why is this outdated' bugs.
Distinguishing state your frontend owns entirely (a modal being open, a form's current input) from state that actually lives on your server/database and is just cached locally — treating them the same way is a common source of full-stack bugs.

Think of it like this
Your notebook vs the notice board
Whatever you scribble in your rough notebook is yours alone. The exam date on the college notice board belongs to the office. You can copy it down, but if the office changes it, your copy is wrong until you look again. Client state is the notebook; server state is the notice board.
Watch it happen
Who owns this data?
Client state
- Owned by this browser, nobody else
- Modal open, form input, selected tab
- useState, Context or Zustand
Server state
- Owned by the database, shared by many
- Orders, profile, cart, comments
- A cached copy that can go stale
Keep UI-only state in React tools. Treat server data as a cache and let a library like TanStack Query manage it.
The big ideas
What you need to know
Client state is instant
You set it and it's immediately true. No loading, no errors, no other users changing it.
Server state is shared
Another tab, device or user can change it at any time. Your copy is a snapshot, not the truth.
Server state needs care
It needs loading and error handling, caching, refetching and invalidation after writes. Libraries handle this well.
Don't copy it into a store
Putting API data into Redux or useState means two copies to keep in sync by hand.
See it in code
Walk through the code
1import { useState } from "react";2import { useQuery } from "@tanstack/react-query";3 4function OrdersPage() {5 // Client state: only this browser cares6 const [filter, setFilter] = useState<"all" | "pending">("all");7 const [drawerOpen, setDrawerOpen] = useState(false);8 9 // Server state: a cached copy of what the database holds10 const orders = useQuery({11 queryKey: ["orders", filter],12 queryFn: () => api<Order[]>(`/orders?status=${filter}`),13 staleTime: 30_000,14 });15 // ...render filter buttons, the drawer, and orders.data / orders.isPending / orders.error16}1/4The filter and the drawer are pure UI. useState is perfect: instant, local, no network.
Two kinds of state, two tools. api is the small fetch helper from the connecting-frontend phase.
Save yourself the pain
Common mistakes
Oops
Fetching orders once and keeping them in a global store forever.
Do this instead
Let a server-state library own them, so they refetch when stale and after you change them.
Oops
Keeping the user's profile both in Context and in a query.
Do this instead
Give each piece of data one owner. Server data comes from the query cache, so read it from there.
Your mission
Sort your state
Quick check
Which of these is server state?
Go deeper
- 1Audit a project's state and label each piece as client state or server state.
- 2Move server state out of a general-purpose store like Redux/Context into React Query if it's mixed in today.
- 3Learn why treating all state the same way tends to cause avoidable complexity.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.