Setup
import * as React from 'react'
import { QueryClient } from '@tanstack/react-query'
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister'
const queryClient = new QueryClient({
defaultOptions: { queries: { gcTime: 24 * 60 * 60 * 1000 } },
})
const persister = createSyncStoragePersister({
storage: window.localStorage,
})
export function AppProviders(props: { children: React.ReactNode }) {
return (
<PersistQueryClientProvider
client={queryClient}
persistOptions={{ persister }}
>
{props.children}
</PersistQueryClientProvider>
)
}
Core Integration Patterns
Resume paused mutations after restore
import { QueryClient } from '@tanstack/react-query'
export const queryClient = new QueryClient()
queryClient.setMutationDefaults(['todos'], {
mutationFn: async (todo: { id: number; title: string }) => todo,
})
export function resumeMutations() {
return queryClient.resumePausedMutations()
}
Align cache lifetime with persistence
import { QueryClient } from '@tanstack/react-query'
export const queryClient = new QueryClient({
defaultOptions: {
queries: { gcTime: 7 * 24 * 60 * 60 * 1000 },
},
})
Use networkMode for offline-first writes
import { useMutation } from '@tanstack/react-query'
export function useSaveDraft() {
return useMutation({
mutationKey: ['saveDraft'],
mutationFn: async (draft: { body: string }) => draft,
networkMode: 'offlineFirst',
})
}
Common Mistakes
HIGH gcTime shorter than maxAge
Wrong:
import { QueryClient } from '@tanstack/react-query'
export const queryClient = new QueryClient({
defaultOptions: { queries: { gcTime: 5 * 60 * 1000 } },
})
Correct:
import { QueryClient } from '@tanstack/react-query'
export const queryClient = new QueryClient({
defaultOptions: { queries: { gcTime: 24 * 60 * 60 * 1000 } },
})
Persisted data can be garbage-collected before the persister maxAge can restore it.
Source: TanStack/query:docs/framework/react/plugins/persistQueryClient.md
CRITICAL Rendering before restore
Wrong:
import { QueryClientProvider, QueryClient } from '@tanstack/react-query'
const queryClient = new QueryClient()
export function App(props: { children: React.ReactNode }) {
return (
<QueryClientProvider client={queryClient}>
{props.children}
</QueryClientProvider>
)
}
Correct:
import { QueryClient } from '@tanstack/react-query'
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
import { createSyncStoragePersister } from '@tanstack/query-sync-storage-persister'
const queryClient = new QueryClient()
const persister = createSyncStoragePersister({ storage: window.localStorage })
export function App(props: { children: React.ReactNode }) {
return (
<PersistQueryClientProvider
client={queryClient}
persistOptions={{ persister }}
>
{props.children}
</PersistQueryClientProvider>
)
}
The persistence provider prevents query fetching while restore is in progress.
Source: TanStack/query:docs/framework/react/plugins/persistQueryClient.md
CRITICAL Missing default mutationFn
Wrong:
import { QueryClient } from '@tanstack/react-query'
export const queryClient = new QueryClient()
Correct:
import { QueryClient } from '@tanstack/react-query'
export const queryClient = new QueryClient()
queryClient.setMutationDefaults(['saveDraft'], {
mutationFn: async (draft: { body: string }) => draft,
})
Paused persisted mutations cannot resume without a serializable mutation key mapped to a default mutationFn.
Source: TanStack/query:docs/framework/react/plugins/persistQueryClient.md
See also: core/tune-defaults-freshness-retries-and-refetching for gcTime and networkMode.