The Easiest Guide on Migrating to React Query v5

Share with a friend:

React Query has been a game-changer for managing server-state in React applications, and with the release of version 5, it’s now even more powerful and user-friendly. If you’re considering migrating your project from React Query v4 to v5, you’re in the right place. This guide will walk you through the process step by step, highlighting the key changes and providing code snippets for an effortless transition.

Introduction to React Query v5

React Query v5 is here, and it’s packed with exciting new features and improvements. This version focuses on streamlining APIs and making it even more intuitive to use. Here are some of the highlights:

  • Simplified Optimistic Updates: Performing optimistic updates is now easier than ever. Leverage returned variables from useMutation without manual cache updates.
  • Infinite Queries with maxPages: Infinite queries can now have a maximum number of pages to store and refetch.
  • Infinite Queries Prefetch Multiple Pages: Infinite Queries can prefetch multiple pages, enhancing performance.
  • Fine-Grained Storage Persister: An experimental feature for fine-grained storage persistence has been added.
  • Typesafe Query Options: Create query options in a typesafe way for improved development experience.
  • New Hooks for Suspense: Dedicated hooks for handling suspense for data fetching have been introduced.

These additions, along with several improvements make v5 a compelling upgrade. However, they are breaking changes to be aware of.

Breaking Changes

Hooks now have a Single Signature

In v5, React Query now supports a single signature format using objects for passing options. Here’s a quick comparison:

Version 4:

// all of these signatures were acceptable in v4
useQuery(key, fn, options)
useQuery({ queryKey, queryFn, ...options })

useMutation({ mutationFn: QUERY_FN });

const queryClient = useQueryClient();
queryClient.refetchQueries({ queryKey: QUERY_KEY });

queryClient.fetchQuery(QUERY_KEY, QUERY_FN);
queryClient.fetchQuery({ queryKey: QUERY_KEY, queryFn: QUERY_FN });

Version 5:

// useQuery(key, fn, options) <no longer works!>
// Only the signature passing an object is acceptable
useQuery({ queryKey, queryFn, ...options })

// useMutation(QUERY_FN); <no longer works!>
useMutation({ mutationFn: QUERY_FN });

const queryClient = useQueryClient();
// queryClient.refetchQueries(QUERY_KEY); <no longer works!>
queryClient.refetchQueries({ queryKey: QUERY_KEY });

// queryClient.fetchQuery(QUERY_KEY, QUERY_FN); <no longer works!>
queryClient.fetchQuery({ queryKey: QUERY_KEY, queryFn: QUERY_FN });

Now, you simply just pass one object. This change simplifies the way you interact with queries, providing a more consistent API.

Callbacks on useQuery Removed

In v5, onSuccess, onError, and onSettled callbacks have been removed from queries. However, they are still available for mutations. Make sure to update your code accordingly.

Version 4:

const query = useQuery(key, fn, {
  onSuccess: handleSuccess,
  onError: handleError,
  onSettled: handleSettled,

Version 5:

const { data, error, status } = useQuery({ queryKey, queryFn });

if (error) {
  return <p>error.message<p>

// OR

if (status == 'error') {
  return <p>error.message<p>

useQuery still returns an object containing the booleans isError, isSuccess and isPending which can be used to conditionally render your view.

RefetchInterval Callback Function Change

The refetchInterval callback function now only receives the query as a parameter. This streamlines how callbacks are invoked.

Version 4:

refetchInterval: (data, query) => ...

Version 5:

refetchInterval: (query) => ...

These are just a few of the significant changes in v5. Be sure to review the complete list of breaking changes in the migration guide for a comprehensive understanding.

Other Changes:

  1. Infinite queries now require initialPageParam
  2. Hydrate has been renamed to HydrationBoundary. Also, useHydrate hook has been removed
  3. The remove method has been removed from useQuery
  4. cacheTime renamed to gcTime
  5. The status loading has been changed to pending and isLoading has been changed to isPending

This is not an exhaustive list. You can view all the changes on React Query’s official migration guide.

Migrating to React Query v5: A Step-by-Step Guide

Now that we have an overview of the changes, let’s walk through the migration process step by step. We’ll provide code snippets comparing v4 and v5 to help you make a smooth transition.

Note: The minimum required version for TypeScript is now 4.7 and the minimum required React version is now 18.0

Step 1: Install React Query v5

Assuming you are using npm, you can use the command below to install the latest version of React Query, which is version 5.4.3 at the time of writing this article.

npm i @tanstack/react-query@latest

Optional: React Query Devtools also received an update. To install the latest version use:

npm i @tanstack/react-query-devtools@latest

Step 2: Fix Breaking Changes

You can fix all the breaking changes manually by going file by file (the section before should help). Or, you can use a codemod to automatically update your codebase.

Codemod for Migration

To facilitate the migration process, a codemod is provided. It automates much of the migration, but it’s essential to review the generated code for edge cases. You can run the codemod using the following commands:

# For .js or .jsx files
npx jscodeshift ./path/to/src/ \
  --extensions=js,jsx \

# For .ts or .tsx files
npx jscodeshift ./path/to/src/ \
  --extensions=ts,tsx \
  --parser=tsx \

Always remember to run prettier and/or eslint after applying the codemod to ensure code formatting consistency.

New Features in v5

Now that you have version 5 and fixed all the breaking changes, here are some of the new features you might find useful.

Simplified Optimistic Updates

Performing optimistic updates is now more straightforward.

const { mutate, isPending, variables } = useMutation({
  mutationFn: (newTodo) =>'/api/data', { text: newTodo }),
  onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),

return (
    // variables is the mutated data
    { isPending && <p>{ variables }</p>}
    <button onClick={() => mutate("Shopping")} style={{ marginLeft: 15 }}>
        Add todo "Shopping"

Infinite Queries with maxPages

To boost site performance, limit the number of stored and refetched pages for infinite queries using the maxPages option. This helps control memory usage while still allowing users to load content efficiently.

  queryFn: ({ pageParam }) => fetchSomething(pageParam),
  initialPageParam: 0,
  maxPages: 5, // Example limit
  getNextPageParam: (lastPage) =>,

Infinite Queries Prefetch Multiple Pages

Infinite Queries can now prefetch multiple pages, improving performance.

  queryFn: ({ pageParam }) => fetchSomething(pageParam),
  initialPageParam: 0,
  prefetchPages: 2, // Example prefetching 2 pages ahead

New Hooks for Suspense

You can use React Query with React’s Suspense to fetch data. Now, there are specific hooks like useSuspenseQuery, useSuspenseInfiniteQuery, and useSuspenseQueries for this. These hooks ensure that data won’t be potentially undefined on type level, and you won’t need status states or error objects. Instead, you’ll use the React.Suspense component. To learn more, visit here.

import {
} from "@tanstack/react-query";

export const Suspense = () => {
  const { data } = useSuspenseQuery({ queryKey: ['todos'], queryFn: () => {} });
  const { data } = useSuspenseInfiniteQuery({
    queryKey: ['todos'],
    queryFn: () => {},
    getNextPageParam: (lastPage) =>,
    initialPageParam: 1,
  const { data } = useSuspenseQueries({
    queries: [{ queryKey: ['todos'], queryFn: () => {} }],


New React Query DevTools v5

React Query Devtools got a cool UI update. Here is a sneak peak:

React Query DevTools v5


Migrating to React Query v5 opens up a world of new possibilities and streamlines the development experience. While there are breaking changes, the benefits far outweigh the effort required to make the transition. Take your time, review the migration guide, and leverage the provided codemod to make the process as smooth as possible.

Share with a friend:

Rajae Robinson

Rajae Robinson is a young Software Developer with over 3 years of work experience building websites and mobile apps. He has extensive experience with React.js and Next.js.

Recent Posts