Performance: dehydrate() + persistence triggered on every cache update β severe blocking with large cache
Describe the bug
When using PersistQueryClientProvider with a large cache (β₯5,000 queries), each setQueryData triggers a full dehydrate() and persistence cycle. This causes noticeable main-thread blocking (1β3 seconds) and complete UI freeze as cache size grows.
Sandbox features:
- Toggle to enable/disable query persistence
- Simulates adding βwork itemsβ (each creates ~5β6 related queries)
Behavior in sandbox:
- Persistence OFF β No lag, even with 10,000+ queries
- Persistence ON β Adding just 100 new work items (~500 queries) causes >1 second freeze when cache already has ~5,000 queries
- Data in sandbox is extremely lightweight β in real apps with richer data, the stall is significantly worse
Root cause
PersistQueryClientProvider subscribes to cache changes and calls persistClient() & dehydrate() on every cache mutation, including every setQueryData. With thousands of queries, this repeatedly serializes the entire cache, blocking the main thread.
Your minimal, reproducible example
https://codesandbox.io/p/sandbox/fragrant-flower-59grjn
Steps to reproduce
Steps to reporduce:
- Click
Add 500 work itemstwice. - When we have 5k total queries, open the performance tab.
- Click
Add 100 work items - Observe the main thread being blocked for more than 1 second by reactQuery internals
Expected behavior
- Persistence should be batched or throttled, not triggered on every setQueryData.
- Alternatively, React Query could maintain an in-memory dehydrated state inside
PersistQueryClientProviderand reuse it, rather than re-serializing the entire cache every time.
How often does this bug happen?
Every time
Screenshots or Videos
Live lag in the code sandbox:
https://github.com/user-attachments/assets/70fc9d1c-2ba9-444e-8d35-d66d114e88cb
Each long task is after adding 100 work items:
Platform
- OS: Mac OS
- Browser: Chrome
- Version: 140.0.7339.133
Tanstack Query adapter
None
TanStack Query version
v5+
TypeScript version
No response
Additional context
Cache Population Pattern
We're using a pattern where list mutations populate related entity caches:
queryClient.setQueryData(["works", "detail", work.id], work);
queryClient.setQueryData(["users", "detail", work.owner.id], work.owner);
queryClient.setQueryData(["teams", "detail", work.team.id], work.team);
...etc
Each of these calls triggers persistence, causing repeated dehydrations and full-cache serialization.
Observations from my app:
We saw react-query blocking the main thread for 6-7 seconds consistently on many list calls after enabling the persistent query cache.
You could try to pass throttleTime?: number to your persister to make it less of an issue, but even then when event happens it might be blocking. Unfortunately this is how default persister was designed to work.
If you are reaching limits and would like to actually solve the problem instead of making it less obvious, you might want to try experimental_createQueryPersister
It will give you fine-grained control over which queries will be stored, and will store them as separate entries in persistent store, basically eliminating the problem you are describing.