← Back to home

Exploring React Query: A Powerful Alternative for Server-Side State Management in React

Understand how React Query can help to simplify data fetching and enhance development experience.

Published at: Jun 5, 2023 5min read
Exploring React Query: A Powerful Alternative for Server-Side State Management in React

React offers a range of tools for managing state. Traditionally, developers have relied on libraries like Redux, Zustand, and the Context API to handle state management.

However, in the last few years React Query has gained attention for its unique approach to server-side state management. Today we will compare React Query with these established tools, and talk about the advantages it brings to server-side state management in React.

But first, we need to understand what is server-side state management and its difference to client-side state.

Understanding Server-Side State Management

In a typical web application, server-side state refers to information fetched from APIs, databases, and other external sources. Effective server-side state management ensures that the application remains in sync with the server and provides a smooth user experience.

Client-side state (a.k.a. global state), on the other hand, refers to data that is particular to the front-end of the application. Usually the data that is stored in client-side state is meant to be used across the application. Some examples are: isAuthenticated, isDarkMode, theme, userData.

One of the advantages of React Query over other solutions for server-side state is that you don’t force asynchronous data to follow the synchronous flow of the application.

It’s also worth mentioning that you can use two tools together to manage the states e.g., Redux for client-side and React-Query for server-side. This is considered a good practice, because it will split different responsibilities between more focused tools.

What is React Query?

React Query is a library specifically designed for managing remote data in React applications. Its key principle is to leverage the power of React’s hooks to simplify asynchronous data fetching, caching, and synchronization.

React Query offers a straightforward API that is easy to understand and use, making it a great option for beginners. Compared to Redux or Zustand, which require additional setup and boilerplate code, React Query simplifies the process of managing server-side state, reducing the learning curve.

The boilerplate code needed to start using React Query is very small:

// 1. Wrapping the component with the provider
const queryClient = new QueryClient();
<QueryClientProvider client={queryClient}>
  <YourComponent />

// 2. Creating a service
const getItems = async () => {
  const response = await fetch("https://api.example.com/items");
  const data = await response.json();
  return data;

// 3. Calling the service in the component
const CACHE_KEY = "items";
const { data, isLoading, isError } = useQuery(CACHE_KEY, getItems);

And that’s it! Now the component is ready to start automatically caching, invalidating and refetching data.

Following the above example, if the user navigates to nother page that has the same getItems call, the data will be served immediately from the cache, because React Query caches the result for a certain amount of time, which is configurable.

React Query vs. Redux

Redux is a widely adopted library for managing global state in React applications. It follows a centralized store approach, where the entire application state is stored in a single JavaScript object.

While Redux excels in managing complex state and enabling time-travel debugging, it often requires developers to write a significant amount of boilerplate code. As a result, Redux can be challenging for beginners to understand.

Nowadays Redux has evolved and it’s much easier to start with, thanks to Redux Toolkit. It also has a library with the same purpose as React Query called RTK Query. It applies different concepts than React Query but the idea behind is quite the same.

// 1. Creating the store
export const store = configureStore({
  reducer: {
    [api.reducerPath]: api.reducer,
    // ... other reducers
  middleware: (getDefaultMiddleware) =>


// 2. Wrapping the component with the redux provider
<Provider store={store}>
  <YourComponent />

// 3. Creating a service
export const api = createApi({
  reducerPath: "api",
  baseQuery: fetchBaseQuery({ baseUrl: "https://api.example.com" }),
  // Endpoints definition
  endpoints: (builder) => ({
    items: builder.query({
      query: () => "items",
    // ... other endpoints

// 4. Calling it in the component
const { data, isLoading, isError } = useItemsQuery();

RTK Query is a powerful library, but when I read the above boilerplate code, it feels too much compared to React Query; there’s a lot of stuff going on that may be abstracted to the developer.

React Query vs. Zustand

Zustand is a lightweight state management library that embraces a more decentralized approach compared to Redux. It provides a simple API for managing state as isolated stores, making it easier to work with and reducing boilerplate code.

Zustand’s simplicity is appealing to beginners, but it lacks built-in server-side data fetching capabilities, which becomes a hurdle for managing server-side state effectively.

// 1. Creating the store
const useStore = create((set) => ({
  items: [],
  fetchItems: async () => {
    try {
      const response = await fetch("https://api.example.com/items");
      const data = await response.json();
      set({ items: data });
    } catch (error) {
      console.error("Error occurred while fetching items:", error);

// 2. Calling it in the component
const { items, fetchItems } = useStore();

Zustand is pretty simple to start with, but its simplicity is also its weakness for dealing with server-side state. Although for managing client-side state it’s a great library: easy to use, lightweight and has a great community.

React Query vs. Context API

The Context API is built into React and allows components to share state without prop drilling. It provides a way to pass data through the component tree without explicitly passing props at every level.

While Context API simplifies state sharing, it does not offer advanced features like caching, background data syncing, or error handling out of the box.

Implementing these features manually can be time-consuming and error-prone. There’s also a considerable amount of boilerplate code needed to make it work.

// 1. Creating the context
export const ServerStateContext = createContext();

// 2. Creating a service
const getItems = async () => {
  const response = await fetch("https://api.example.com/items");
  const data = await response.json();
  return data;

// 3. Creating the provider
export const ServerStateProvider = ({ children }) => {
  const [items, setItems] = useState([]);

  useEffect(() => {
    const fetchItems = async () => {
      try {
        const data = await api.getItems();
      } catch (error) {
        console.error("Error occurred while fetching items:", error);

    // 4. Calling the service
  }, []);

  return (
    <ServerStateContext.Provider value={{ items }}>

// 5. Calling the service in the component
const { items } = useContext(ServerStateContext);

Depending on how you implement Context API, you may see a lot of unnecessary re-renders, which can be hard to debug for beginners.

Final thoughts

React Query emerges as a powerful alternative for server-side state management in React applications. It offers simplified data fetching, intelligent caching, background data syncing, and error handling, which can significantly improve development productivity.

Compared to Redux, Zustand, and Context API, React Query provides a more beginner-friendly approach while maintaining advanced features necessary for server-side state management.

By combining React Query and other client-side state manager mentioned above, it’s possible to create robust and performant applications, separating the concerns of state management and having a more defined architecture.

Remember, the choice of state management tool ultimately depends on the specific needs of your application. It’s essential to evaluate the trade-offs and consider factors like project size, complexity, and development team experience.

If you have any feedback or suggestions, send me an email

Great coding!