Stop 11 · State ManagementTopics · 1/5

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.

The blob character holds a notebook and pencil while looking up at a pinboard full of notices.

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

tsx
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

~25 min

Quick check

Which of these is server state?

Go deeper

  1. 1Audit a project's state and label each piece as client state or server state.
  2. 2Move server state out of a general-purpose store like Redux/Context into React Query if it's mixed in today.
  3. 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.

Download
InsideEdge