How to Use Zustand with Third-Party Libraries Like React Query

You can use Zustand to manage local UI state while letting React Query handle server state, syncing the two via callbacks like onSuccess to combine Zustand's lightweight reactivity with robust data fetching.

Zustand's minimal API makes it ideal for composing with other libraries in the React ecosystem. Because the pmndrs/zustand repository exposes only a simple create function and an unobtrusive useStore hook, you can safely integrate it alongside React Query without architectural conflicts.

Why Zustand Integrates Seamlessly with Third-Party Libraries

Zustand is intentionally framework-agnostic. According to the repository's README.md, the core philosophy emphasizes that a store is just a plain JavaScript object with a subscription mechanism. This design means Zustand does not prescribe how data enters the store, allowing you to populate it from any source—including React Query's cache.

The create function documented in docs/reference/apis/create.md returns a hook that reads from the store, while the underlying store object exposes setState methods that any callback can invoke. This separation of concerns lets React Query manage the network layer while Zustand provides the reactive UI layer.

Separating Server State and Client State

When you use Zustand with React Query, assign distinct responsibilities to each library to avoid duplication and stale data.

Concern Tool Implementation Pattern
Server state (caching, refetching, background updates) React Query Use useQuery to fetch data and onSuccess to push results into Zustand.
Local UI state (modals, forms, filters) Zustand Store flags and temporary values in a create store accessed via useStore.
Optimistic updates Both Update Zustand immediately for UI responsiveness, then sync React Query's cache via setQueryData or invalidateQueries.
Persistence Zustand middleware Use persist from docs/reference/middlewares/persist.md to save UI state across reloads while React Query refreshes server data.

Practical Implementation: Zustand with React Query

Setting Up the Zustand Store

First, create a store that will hold the data fetched by React Query. The useStore hook documented in docs/reference/hooks/use-store.md allows components to subscribe to specific slices.

// src/store/useTodoStore.ts
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';

type Todo = { id: number; title: string; completed: boolean };

interface TodoStore {
  todos: Todo[];
  setTodos: (todos: Todo[]) => void;
  addTodo: (todo: Todo) => void;
  toggle: (id: number) => void;
}

export const useTodoStore = create<TodoStore>()(
  devtools((set) => ({
    todos: [],
    setTodos: (todos) => set({ todos }),
    addTodo: (todo) =>
      set((state) => ({ todos: [...state.todos, todo] })),
    toggle: (id) =>
      set((state) => ({
        todos: state.todos.map((t) =>
          t.id === id ? { ...t, completed: !t.completed } : t
        ),
      })),
  }))
);

Syncing Query Data to Zustand

Use React Query's onSuccess callback to write fetched data into the Zustand store. This pattern keeps your components decoupled from the fetching logic while providing instant access to data via Zustand's stable API

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →