notifyOnChangeProps no longer accepts 'tracked' value
The notifyOnChangeProps option no longer accepts the string 'tracked' as a value in v4. The 'tracked' behavior is now the default for all queries. Remove this option from any queries that use it. To opt-out of smart tracking and re-render on all changes, pass notifyOnChangeProps: 'all' instead.
notifyOnChangePropsExclusion removed
The notifyOnChangePropsExclusion option has been completely removed in v4. Since 'tracked' behavior is now the default, this option is no longer necessary. Remove any references to notifyOnChangePropsExclusion from your code.
cancelRefetch defaults to true for all refetch methods
In v4, the cancelRefetch option defaults to true for refetchQueries, invalidateQueries, resetQueries, and the refetch function from useQuery. This means calling these methods multiple times will cancel previous fetches and restart. For fetchNextPage and fetchPreviousPage in useInfiniteQuery, cancelRefetch also defaults to true. This is a change from v3 where it defaulted to false. You can opt-out by explicitly passing cancelRefetch: false.
Query and mutation keys must be arrays
In v4, query and mutation keys must always be arrays. In v3, they could be strings or arrays, but v4 standardizes on arrays only. Change code like useQuery('todos', fetchTodos) to useQuery(['todos'], fetchTodos). A codemod is available to automate this migration.
Query filter type changed from boolean flags to single type property
In v4, query filters use a single type property instead of separate active/inactive boolean flags. Replace active/inactive boolean flags with type: 'active' | 'inactive' | 'all'. The filter defaults to 'all', which matches all queries regardless of active status.
refetchActive and refetchInactive replaced with refetchType
In v4, the refetchActive and refetchInactive boolean flags on invalidateQueries have been replaced with a single refetchType property. Use refetchType: 'active' | 'inactive' | 'all' | 'none'. This defaults to 'active' (matching the previous default of refetchActive: true). Use 'none' to disable refetching completely.
onSuccess no longer called from setQueryData
In v4, the onSuccess callback is not triggered when setQueryData is called. The onSuccess callback is now only called when an actual request is made. This prevents infinite loops and confusing behavior. To react to data changes, use a useEffect hook with data as a dependency instead.
Persister plugins renamed and moved to separate packages
In v4, the experimental persister plugins have been renamed and moved to separate packages. createWebStoragePersistor is now createSyncStoragePersister from @tanstack/query-sync-storage-persister. createAsyncStoragePersistor is now createAsyncStoragePersister from @tanstack/query-async-storage-persister. persistQueryClient is imported from @tanstack/react-query-persist-client. The Persistor interface has been renamed to Persister.
Promise cancel method no longer supported
The old cancel method that could be defined on promises is no longer supported in v4. Use the AbortController API instead for query cancellation. The query function receives an AbortSignal instance that you can use to support cancellation.
TypeScript version requirement v4.1 or greater
React Query v4 requires TypeScript v4.1 or greater. Update your TypeScript version if you are using an earlier version.
setLogger removed, use QueryClient logger option instead
In v4, the global setLogger function has been removed. Instead, pass a logger option when creating a QueryClient: new QueryClient({ logger: customLogger }). This replaces both the import of setLogger from react-query and the separate setLogger() call.
Server-side cacheTime defaults to Infinity
In v4, the default cacheTime for server-side React Query is set to Infinity, disabling manual garbage collection. This prevents high memory consumption and hanging processes waiting for garbage collection. In v3, the default was 5 minutes. This change only affects server-side usage such as with Next.js. If you manually set cacheTime, this does not affect you.
Production error logging disabled
Starting with v4, React Query no longer logs errors (such as failed fetches) to the console in production mode. Errors will still be logged in development mode.
QueryCacheNotifyEvent type names changed
In v4, the QueryCacheNotifyEvent type names have been shortened. 'queryAdded' is now 'added', 'queryRemoved' is now 'removed', and 'queryUpdated' is now 'updated'. This applies when manually subscribing to the QueryCache via queryCache.subscribe().
Hydration utilities moved to main react-query export
In v4, hydration utilities (dehydrate, hydrate, useHydrate, Hydrate) have been moved to the main @tanstack/react-query export. The separate react-query/hydration export path has been removed. Import these directly from @tanstack/react-query instead.
Removed undocumented queryClient methods
In v4, the undocumented methods cancelMutations and executeMutation have been removed from queryClient. The mutation.cancel method was also removed because it did not actually cancel outgoing requests. The query.setDefaultOptions method was also removed as it was unused.
src/react directory renamed to src/reactjs
In v4, the src/react directory has been renamed to src/reactjs to avoid conflicts with Jest configurations that can confuse the react directory with the react module. If you were importing directly from 'react-query/react', update to import from '@tanstack/react-query/reactjs'.
React 18 first-class support
React Query v4 includes first-class support for React 18 and its new concurrent features.
Tracked queries enabled by default
In v4, React Query defaults to tracking query properties, which was introduced in v3.6.0 as an opt-in feature. This tracking provides render optimization by only re-rendering when tracked properties change, reducing unnecessary re-renders.
setQueryData bailing out with undefined
In v4, when using the functional updater form of setQueryData, you can return undefined to bail out of the update. This is useful when previousValue is undefined (meaning no cached entry exists) and you cannot or do not want to create one, such as when toggling a todo item.
Mutation cache garbage collection
In v4, mutations can now be automatically garbage collected like queries. The default cacheTime for mutations is 5 minutes.
Custom contexts for multiple React Query providers
In v4, custom React contexts can be specified when creating hooks and providers to pair them correctly. This is critical when multiple React Query provider instances exist in the component tree and you need to ensure hooks use the correct provider. Create a context, pass it to QueryClientProvider as the context prop, and then pass the same context to useQuery or other hooks via the context option in their configuration.
Codemod for import migration
To migrate imports from react-query to @tanstack/react-query, use the codemod with command: npx jscodeshift ./path/to/src/ --extensions=js,jsx --transform=./node_modules/@tanstack/react-query/codemods/v4/replace-import-specifier.js for JavaScript files, or --extensions=ts,tsx --parser=tsx for TypeScript files. The codemod only changes imports; devtools package must be installed separately. After applying, run prettier and eslint to fix formatting.
Codemod for query key transformation
To convert query keys from strings to arrays, use the codemod with command: npx jscodeshift ./path/to/src/ --extensions=js,jsx --transform=./node_modules/@tanstack/react-query/codemods/v4/key-transformation.js for JavaScript files, or --extensions=ts,tsx --parser=tsx for TypeScript files. This is a best-effort migration tool; review generated code thoroughly. After applying, run prettier and eslint to fix formatting.
Idle state removed, replaced with loading and fetchStatus
The 'idle' status has been removed in v4. Queries that were previously in 'idle' state are now in 'loading' state with fetchStatus: 'idle'. This affects disabled queries that don't have data yet. Use isInitialLoading instead of isLoading to detect initial loading state, including disabled queries.
useQueries now accepts object with queries property
The useQueries hook API changed in v4. Instead of passing an array directly, pass an object with a 'queries' property containing the array. Old: useQueries([{queryKey1, queryFn1, options1}, {queryKey2, queryFn2, options2}]). New: useQueries({queries: [{queryKey1, queryFn1, options1}, {queryKey2, queryFn2, options2}]}).
Undefined is an illegal cache value for successful queries
In v4, undefined cannot be returned as a successful query result. Returning undefined from a queryFn will be transformed to a failed Promise at runtime, resulting in an error. This prevents accidental Promise<void> bugs and infinite loops that could occur when combined with onSuccess callbacks and setQueryData. At the type level, this is disallowed by TypeScript.
Package renamed to @tanstack/react-query
The package react-query has been renamed to @tanstack/react-query in v4. You must uninstall the old package and install the new one. The devtools package also moved from react-query/devtools to a separate package @tanstack/react-query-devtools. Update all imports from 'react-query' to '@tanstack/react-query' and from 'react-query/devtools' to '@tanstack/react-query-devtools'.
v5 minimum React version is 18.0
TanStack Query v5 requires React 18.0 or later because it uses the useSyncExternalStore hook which is only available in React 18.0 and later.
v5 removed contextSharing prop from QueryClientProvider
In v5, the contextSharing prop has been removed from QueryClientProvider. This was previously used to share the query client context across different bundles or microfrontends. Instead, directly pass a shared custom queryClient instance to achieve the same isolation.
v5 no longer uses unstable_batchedUpdates
In v5, unstable_batchedUpdates is no longer automatically set as the batching function in react-query because it is a noop in React 18. If your framework supports a custom batching function, set it using notifyManager.setBatchNotifyFunction.
v5 renamed Hydrate component to HydrationBoundary
In v5, the Hydrate component has been renamed to HydrationBoundary. The useHydrate hook has been removed. HydrationBoundary now only hydrates queries, not mutations. To hydrate mutations, use the low-level hydrate API or persistQueryClient plugin.
v5 hydration timing changed for existing cache queries
In v5, the timing for hydration has changed. New queries are still hydrated in the render phase for SSR compatibility. However, queries that already exist in the cache are now hydrated in an effect. This may cause a flash of old data when using Server Components with page navigation, but is necessary to avoid prematurely updating content before a page transition is fully committed.
v5 query defaults merging changed
In v5, queryClient.getQueryDefaults now merges together all matching registrations instead of returning only the first matching one. As a result, calls to queryClient.setQueryDefaults should be ordered with increasing specificity, from most generic key to least generic one.
v5 new simplified optimistic updates pattern
In v5, optimistic updates can be simplified by leveraging the returned variables from useMutation. When isPending is true, you can display addTodoMutation.variables in the UI to show the optimistic update without writing directly to the cache. This works best when there's only one place showing the optimistic update.
v5 renamed useErrorBoundary to throwOnError
In v5, the useErrorBoundary option has been renamed to throwOnError to make it more framework-agnostic and to avoid confusion with React's 'use' prefix for hooks and the ErrorBoundary component name.
v5 renamed cacheTime to gcTime
In v5, the cacheTime option has been renamed to gcTime. The gcTime option refers to garbage collection time - the time after which unused queries are removed from the cache. cacheTime had a misleading name because it only affects unused queries, not queries that are actively being used.
v5 hooks now require object parameter only
In v5, useQuery, useInfiniteQuery, useMutation, useIsFetching, and useIsMutating no longer accept multiple overloads. They only support the object format. useQuery requires an object with queryKey and queryFn properties instead of the v4 style useQuery(key, fn, options).
v5 queryClient methods require object parameter with queryKey
In v5, queryClient methods like isFetching, ensureQueryData, getQueriesData, setQueriesData, removeQueries, resetQueries, cancelQueries, invalidateQueries, refetchQueries, fetchQuery, prefetchQuery, fetchInfiniteQuery, and prefetchInfiniteQuery all require an object as the first parameter with queryKey property instead of separate key and filters parameters.
v5 queryCache methods require object parameter with queryKey
In v5, queryCache.find and queryCache.findAll methods require an object parameter with queryKey property instead of separate key and filters parameters.
v5 getQueryData and getQueryState accept only queryKey
In v5, queryClient.getQueryData and queryClient.getQueryState methods now accept only queryKey as a parameter. The filters parameter has been removed.
v5 codemod for remove-overloads migration
TanStack Query v5 provides a codemod to help migrate from multiple overloads to object-only syntax. Run it with: npx jscodeshift@latest ./path/to/src/ --extensions=js,jsx --transform=./node_modules/@tanstack/react-query/build/codemods/src/v5/remove-overloads/remove-overloads.cjs for JavaScript files, or use --extensions=ts,tsx --parser=tsx for TypeScript files. The codemod is a best-effort tool that may not handle all edge cases, so review generated code and run prettier/eslint afterward.
v5 removed query callbacks onSuccess, onError, onSettled
In v5, the onSuccess, onError, and onSettled callbacks have been removed from useQuery and QueryObserver. These callbacks were removed for queries but remain available for mutations. See the RFC at https://github.com/TanStack/query/discussions/5279 for the motivations and alternatives.
v5 refetchInterval callback signature changed
In v5, the refetchInterval callback function now only receives the query as a parameter. Previously it received both data and query. The callback signature changed from ((data: TData | undefined, query: Query) => number | false | undefined) to ((query: Query) => number | false | undefined). Data can still be accessed via query.state.data, but it will not be transformed by select.
v5 removed remove method from useQuery
In v5, the remove method on the query object returned from useQuery has been removed. To remove a query, use queryClient.removeQueries({queryKey: key}) instead.
v5 minimum TypeScript version is 4.7
TanStack Query v5 requires TypeScript 4.7 or later, primarily for a fix in type inference around TypeScript issue #43371.
v5 removed isDataEqual option
In v5, the isDataEqual option has been removed from useQuery. To achieve the same functionality, pass a function to structuralSharing instead. For example: structuralSharing: (oldData, newData) => customCheck(oldData, newData) ? oldData : replaceEqualDeep(oldData, newData)
v5 removed custom logger support
In v5, custom loggers have been removed. They were already deprecated in v4 and only had an effect in development mode.
v5 uses ECMAScript private class fields
In v5, TanStack Query now uses ECMAScript private class fields and methods instead of TypeScript-only privacy. This means private fields and methods are now truly private at runtime and cannot be accessed from outside.
v5 Error is default type instead of unknown
In v5, TypeScript now defaults to Error as the error type instead of unknown. This makes it easier to work with errors in most cases. To throw something that isn't an Error, you must set the error generic type explicitly, for example: useQuery<number, string>
v5 removed keepPreviousData in favor of placeholderData
In v5, the keepPreviousData option and isPreviousData flag have been removed. Use placeholderData with keepPreviousData function or an identity function instead: placeholderData: keepPreviousData or placeholderData: (previousData) => previousData. The replacement uses isPlaceholderData flag instead of isPreviousData. However, placeholderData puts you into success state (unlike keepPreviousData which preserved the previous query's status), and dataUpdatedAt will be 0 (unlike keepPreviousData which preserved the previous data's timestamp).
v5 uses visibilitychange event instead of focus
In v5, window focus refetching no longer listens to the focus event. Instead, it uses the visibilitychange event exclusively. This is possible because v5 only supports browsers that support visibilitychange.
v5 no longer uses navigator.onLine
In v5, network status detection no longer relies on navigator.onLine due to false negatives in Chromium-based browsers. Instead, TanStack Query starts with online: true and only listens to online and offline events. This reduces false negatives but may increase false positives for offline apps that load via service workers.
v5 removed custom context prop
In v5, the custom context prop has been removed from useQuery and other hooks. Instead, pass a custom queryClient instance directly as a parameter. This enables better isolation for MicroFrontends and provides the same functionality in a framework-agnostic way.
v5 removed refetchPage in favor of maxPages
In v5, the refetchPage option for infinite queries has been removed in favor of a new maxPages option. The maxPages option limits the number of pages stored in query data and refetched, avoiding UI inconsistencies from refetching all pages.
v5 dehydrate API simplified
In v5, the dehydrate API has been simplified. The boolean options dehydrateMutations and dehydrateQueries have been removed and replaced with function equivalents shouldDehydrateQuery and shouldDehydrateMutation. Queries and Mutations are always dehydrated by default according to the default function implementation. To disable dehydration, pass () => false.
v5 infinite queries require initialPageParam
In v5, infinite queries now require an explicit initialPageParam option. This is used as the pageParam for the first page. Previously, undefined was passed as pageParam, but this was not serializable. The queryFn should no longer have a default value for pageParam.
v5 removed manual mode for infinite queries
In v5, manual mode for infinite queries has been removed. Previously, you could override pageParams by passing a pageParam value to fetchNextPage or fetchPreviousPage. This feature didn't work with refetches and wasn't widely used. getNextPageParam is now required for infinite queries.
v5 null from getNextPageParam indicates no further page
In v5, returning null from getNextPageParam or getPreviousPageParam now indicates that there is no further page available. Previously, you had to explicitly return undefined.
v5 no retries on the server
In v5, on the server, retry now defaults to 0 instead of 3. This prevents retries on the server for prefetching and suspense-enabled queries that execute directly on the server (available since React 18).