Managing State the Right Way: Zustand for Client State & TanStack Query for Server State

Iβm Arjun Saxena, a passionate software developer specializing in web engineering. I believe in writing code that creates real solutions to real problems. I love building efficient, user-friendly applications and constantly push myself to learn new technologies. Beyond coding, I enjoy sharing knowledge and growing together with others in the tech community.
When building modern React apps, state management becomes very important.
But hereβs the mistake many beginners make π
π They try to manage everything with one tool.
In reality, there are two different types of state:
Client-side state
Server-side state
In this blog, youβll learn:
What client state vs server state really means
Why Zustand is perfect for global client state
Why TanStack Query is best for server state
How caching & optimistic UI work (in easy words)
When to use what β
First, Understand State with a Simple Analogy π§
Think of a food delivery app π
Client State (Local App State)
Things that exist only inside your app:
Dark / light mode
Sidebar open or closed
Logged-in user info
Selected filters
π This is client-side state
Server State (Remote Data)
Things that come from a server / API:
Products list
User profile from backend
Orders data
Notifications
π This is server-side state
Client State vs Server State (Very Clear Comparison)
| Client State | Server State |
| Lives in browser | Lives on server |
| UI-related | Data-related |
| Controlled by user actions | Controlled by API |
| Zustand is great here | TanStack Query is great here |
Using Zustand for Global Client-Side State π»
Zustand is:
Simple
Lightweight
No boilerplate
Perfect for UI & global state
Creating a Zustand Store (Easy Example)
Step 1: Install
npm install zustand
Step 2: Create a Store
import { create } from "zustand";
const useStore = create((set) => ({
count: 0,
increase: () => set((state) => ({ count: state.count + 1 })),
decrease: () => set((state) => ({ count: state.count - 1 }))
}));
Step 3: Use It in Component
function Counter() {
const { count, increase, decrease } = useStore();
return (
<>
<h2>Count: {count}</h2>
<button onClick={increase}>+</button>
<button onClick={decrease}>-</button>
</>
);
}
π Thatβs it!
No reducers, no actions, no providers.
When Should You Use Zustand?
Use Zustand for:
Theme (dark / light)
Auth state (logged in or not)
Cart state
UI toggles
β Do NOT use Zustand for fetching server data
Using TanStack Query for Server-Side State π
TanStack Query (React Query):
Fetches data
Caches data
Syncs data with server
Handles loading & error states automatically
Installing TanStack Query
npm install @tanstack/react-query
Basic Data Fetching Example
Setup Query Client
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<Users />
</QueryClientProvider>
);
}
Fetch Data with useQuery
import { useQuery } from "@tanstack/react-query";
function Users() {
const { data, isLoading, error } = useQuery({
queryKey: ["users"],
queryFn: () =>
fetch("https://jsonplaceholder.typicode.com/users")
.then(res => res.json())
});
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error!</p>;
return data.map(user => <p key={user.id}>{user.name}</p>);
}
βοΈ Loading handled
βοΈ Error handled
βοΈ Data cached
Caching: Why TanStack Query Is Powerful β‘
Once data is fetched:
It is stored in cache
No extra API calls
Fast UI
π If user revisits the page, data loads instantly.
Optimistic UI (Magic Explained Simply β¨)
Optimistic UI means:
βUpdate the UI before server confirmsβ
Real-Life Example π
You click Like π
UI updates instantly, even before API response.
Simple Optimistic Update Example
useMutation({
mutationFn: addTodo,
onMutate: async (newTodo) => {
queryClient.setQueryData(["todos"], old => [...old, newTodo]);
}
});
π User feels app is fast & smooth
Zustand + TanStack Query Together π€
Best practice:
Zustand β UI & global client state
TanStack Query β API & server state
They solve different problems.
Architecture Diagram Idea π‘
User Action
β
UI Component
β
Zustand (Client State)
β
TanStack Query (Server State)
β
API / Backend




