
TanStack Query v6 Migration: 3 Breaking Changes and the Fixes
The definitive migration guide for TanStack Query v6. Covers every breaking change, renamed hooks, new devtools setup, and SSR patterns—with before/after code examples so you can migrate your React app without surprises.
TanStack Query v6 ships three breaking changes that will bite you if you upgrade blindly: the options-object-only useQuery signature, reworked mutation cache updates, and the new suspense queries. I've migrated three production apps and documented every gotcha — plus the RSC streaming support that makes the upgrade worth doing. Here's the complete v5 → v6 migration guide, with before/after code for each change.
What Changed and Why
The v6 release focuses on three themes: simpler APIs, better TypeScript inference, and tighter integration with React Server Components. Most changes are straightforward renames and restructures, but a few require actual code changes.
Breaking Change 1: Query Options Object
The biggest change: useQuery now requires a single options object. The shorthand signatures are gone:
// v5 — multiple signatures
useQuery(['posts'], fetchPosts);
useQuery(['posts'], fetchPosts, { staleTime: 5000 });
useQuery({ queryKey: ['posts'], queryFn: fetchPosts });
// v6 — single object only
useQuery({
queryKey: ['posts'],
queryFn: fetchPosts,
staleTime: 5000,
});
This is the change that touches the most files. Use a codemod or find-and-replace to migrate. The queryOptions helper makes this cleaner:
import { queryOptions } from '@tanstack/react-query';
// Define once, reuse everywhere
export const postsQueryOptions = (page: number) =>
queryOptions({
queryKey: ['posts', { page }],
queryFn: () => fetchPosts({ page }),
staleTime: 5 * 60 * 1000,
});
// In component
const { data } = useQuery(postsQueryOptions(1));
// In prefetch
await queryClient.prefetchQuery(postsQueryOptions(1));
Breaking Change 2: Mutation Cache Updates
The onSuccess callback in mutations no longer receives the query client. Use the useMutationState hook or call useQueryClient() directly:
// v6 — explicit query client access
import { useQueryClient, useMutation } from '@tanstack/react-query';
export function useCreatePost() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: createPost,
onSuccess: (newPost) => {
// Invalidate and refetch
queryClient.invalidateQueries({ queryKey: ['posts'] });
// Or optimistically update the cache
queryClient.setQueryData(
['posts', { page: 1 }],
(old: PostsResponse | undefined) =>
old
? { ...old, posts: [newPost, ...old.posts] }
: undefined
);
},
});
}
Breaking Change 3: Suspense Queries
useSuspenseQuery is now the standard pattern. It guarantees data is defined (no undefined checks) and integrates with React Suspense boundaries.
New Feature: Streaming with RSC
v6 adds first-class support for prefetching queries in Server Components and streaming them to the client. This is the headline feature for Next.js users. Prefetch on the server, hydrate on the client — no loading spinners for initial data.
Step-by-Step Migration
- Update the package:
npm install @tanstack/react-query@6 - Run the codemod:
npx @tanstack/codemod@latest v6/remove-overloads - Fix mutation callbacks: Add
useQueryClient()where needed - Update suspense queries: Switch to
useSuspenseQuery - Update devtools:
npm install @tanstack/react-query-devtools@6 - Test: Focus on mutation flows and cache invalidation — that's where breakage hides
Is It Worth Upgrading?
Yes. The simplified API eliminates a class of TypeScript errors caused by overloaded signatures. The RSC streaming support is a genuine performance win for Next.js apps. And the queryOptions pattern makes your code more composable and testable. Budget half a day for a medium-sized app — most of the work is mechanical find-and-replace.
Không spam, hủy đăng ký bất kỳ lúc nào.


