Using Zustand for Global Client-Side State & TanStack Query for Server-Side States


Introduction
State management is one of the most important concepts in building modern React applications. But not all state is the same. Some lives in the client (like toggling a dark mode switch), while other state must come from a server (like fetching a list of users).
In this blog, we’ll explore:
✅ Client-side state with Zustand
✅ Server-side state with TanStack Query
✅ How to combine them for scalable apps
Think of it like two brains working together — one handling quick local actions, and the other syncing data with the outside world.
🌐 Client State vs Server State
Before diving into code, let’s clarify:
Aspect | Client State | Server State |
Where it lives | Inside the app (memory/store) | On backend/database |
Examples | Theme, modals, cart count | User list, product data |
Sync needed? | No (app-only) | Yes (needs backend sync) |
Best Tool | Zustand, Redux, Context | TanStack Query, SWR |
👉 Rule of thumb: Use Zustand for UI-driven, temporary state. Use TanStack Query for remote, async data.
🐻 Zustand for Global Client-Side State
Zustand (German for “state”) is a lightweight but powerful state manager. Unlike Redux, it avoids boilerplate and lets you build simple stores with just a few lines of code.
🔹 Creating a Store
import { create } from 'zustand'
const useThemeStore = create((set) => ({
darkMode: false,
toggleDarkMode: () => set((state) => ({ darkMode: !state.darkMode }))
}))
export default useThemeStore
🔹 Using the Store in a Component
import useThemeStore from './store'
function Navbar() {
const { darkMode, toggleDarkMode } = useThemeStore()
return (
<nav className={darkMode ? "bg-black" : "bg-white"}>
<button onClick={toggleDarkMode}>
Toggle Dark Mode
</button>
</nav>
)
}
💡 That’s it! No reducers, no actions, no context providers — just a store and a hook.
⚡ TanStack Query for Server-Side State
While Zustand shines locally, TanStack Query (React Query) is the gold standard for managing async server state. It automatically handles caching, refetching, background updates, and even optimistic UI updates.
🔹 Fetching Data
import { useQuery } from '@tanstack/react-query'
function Users() {
const { data, isLoading, error } = useQuery({
queryKey: ['users'],
queryFn: () => fetch('/api/users').then(res => res.json())
})
if (isLoading) return <p>Loading...</p>
if (error) return <p>Error fetching users</p>
return (
<ul>
{data.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
)
}
🔹 Optimistic Updates (Mutation Example)
import { useMutation, useQueryClient } from '@tanstack/react-query'
function AddUser() {
const queryClient = useQueryClient()
const mutation = useMutation({
mutationFn: (newUser) =>
fetch('/api/users', {
method: 'POST',
body: JSON.stringify(newUser),
}),
onMutate: async (newUser) => {
// Optimistic update
await queryClient.cancelQueries(['users'])
const previousUsers = queryClient.getQueryData(['users'])
queryClient.setQueryData(['users'], old => [...old, newUser])
return { previousUsers }
},
onError: (err, newUser, context) => {
queryClient.setQueryData(['users'], context.previousUsers)
},
onSettled: () => {
queryClient.invalidateQueries(['users'])
}
})
return (
<button onClick={() => mutation.mutate({ id: Date.now(), name: 'New User' })}>
Add User
</button>
)
}
⚡ This way, the UI updates instantly while the server request is still happening — and gracefully rolls back on errors.
🔗 Zustand + TanStack Query: A Perfect Pair
In real-world apps, you’ll often need both client + server states. For example:
Zustand manages a sidebar toggle or selected filters
TanStack Query fetches and caches products based on those filters
🔹 Architecture Diagram Idea
[ UI Components ]
|
Zustand Store (UI State)
|
TanStack Query (Server State)
|
Backend / API
This separation of concerns makes apps scalable, predictable, and performant.
✅ Conclusion
Use Zustand for global client-side state (toggles, theme, UI states).
Use TanStack Query for server-side data (fetching, caching, mutations).
Combine both for a robust, modern state management stack.
By mastering both tools, you’ll simplify your state management and make your React apps faster and more reliable.
Subscribe to my newsletter
Read articles from prashant chouhan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
