useQuery hook is the primary way to fetch data in TanStack Query. It manages the lifecycle of a query, including caching, background updates, and stale data management.
Import
import { useQuery } from '@tanstack/react-query'
Signature
function useQuery<
TQueryFnData = unknown,
TError = DefaultError,
TData = TQueryFnData,
TQueryKey extends QueryKey = QueryKey,
>(
options: UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
queryClient?: QueryClient,
): UseQueryResult<TData, TError>
Parameters
The query options object.
Show properties
Show properties
A unique key for the query. Must be an array. The query will automatically update when this key changes.
queryKey: ['todos']
queryKey: ['todo', todoId]
queryKey: ['todos', { status, page }]
The function that the query will use to request data. Must return a promise that resolves data or throws an error.Receives a
QueryFunctionContext object with:queryKey: TQueryKey- The query keysignal: AbortSignal- AbortSignal instance for query cancellationmeta: QueryMeta | undefined- Optional query metadataclient: QueryClient- The QueryClient instance
queryFn: ({ queryKey, signal }) => fetchTodos(queryKey[1], signal)
Set to
false to disable automatic execution of the query. Useful for dependent queries.enabled: !!userId
The time in milliseconds after data is considered stale. When set to
Infinity or 'static', data will never be considered stale.staleTime: 5000 // 5 seconds
staleTime: Infinity
staleTime: 'static'
The time in milliseconds that unused/inactive cache data remains in memory. When a query’s cache becomes unused or inactive, that cache data will be garbage collected after this duration. Set to
Infinity to disable garbage collection.gcTime: 1000 * 60 * 60 * 24 // 24 hours
If set to a number, the query will continuously refetch at this frequency in milliseconds.
refetchInterval: 1000 // refetch every second
If set to
true, the query will continue to refetch while the browser tab is in the background.If set to
true, the query will refetch on window focus if the data is stale. If set to 'always', the query will always refetch on window focus.If set to
true, the query will refetch on reconnect if the data is stale. If set to 'always', the query will always refetch on reconnect.If set to
true, the query will refetch on mount if the data is stale. If set to 'always', the query will always refetch on mount. If set to false, will disable additional instances of a query to trigger background refetch.If
false, failed queries will not retry. If true, failed queries will retry infinitely. If set to a number, failed queries will retry until the failure count meets that number.retry: 3
retry: false
retry: (failureCount, error) => failureCount < 3 && error.status !== 404
This function receives a
failureCount integer and the actual error and returns the delay to apply before the next attempt in milliseconds.retryDelay: 1000 // Always wait 1 second
retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000)
This option can be used to transform or select a part of the data returned by the query function.
select: data => data.items
If set, this value will be used as the initial data for the query cache (as long as the query hasn’t been created or cached yet).
initialData: { items: [] }
initialData: () => getCachedData()
If set, this value will be used as the time (in milliseconds) of when the
initialData itself was last updated.placeholderData
TData | ((previousData: TData | undefined, previousQuery: Query | undefined) => TData | undefined)
If set, this value will be used as the placeholder data for this particular query observer while the query is still in the pending state.
placeholderData: { items: [] }
placeholderData: (previousData) => previousData
structuralSharing
boolean | ((oldData: unknown | undefined, newData: unknown) => unknown)
default:"true"
Set to
false to disable structural sharing between query results. Set to a function to implement custom structural sharing logic.Set to
true to throw errors instead of setting the error property. Can be set to a function to conditionally throw errors.throwOnError: true
throwOnError: (error) => error.status >= 500
'online'- Queries will not fire unless there is a network connection'always'- Queries will fire regardless of network connection status'offlineFirst'- Queries will fire immediately, but if they fail due to network, they will be paused and retried when the connection is restored
If set to
false, the query will not be retried on mount if it contains an error.If set, the component will only re-render if any of the listed properties change. When set to
'all', the component will re-render whenever the query is updated.notifyOnChangeProps: ['data', 'error']
Optional metadata that can be used in other places like the global query cache callbacks.
meta: { loggingContext: 'todos' }
Use this to override the default QueryClient.
Returns
The query result object.
Show properties
Show properties
The last successfully resolved data for the query.
The error object for the query, if an error was thrown. Defaults to
null.'pending'- No cached data and no query attempt was finished yet'error'- The query attempt resulted in an error'success'- The query has received a response with no errors
'fetching'- The queryFn is executing (initial pending or background refetch)'paused'- The query wanted to fetch, but has been paused'idle'- The query is not fetching
true if there’s no cached data and no query attempt was finished yet.true whenever the first fetch for a query is in-flight. Same as isFetching && isPending.true whenever the queryFn is executing, including initial pending and background refetches.true if the query has received a response with no errors and is ready to display its data.true if the query attempt resulted in an error.true whenever a background refetch is in-flight (does not include initial pending). Same as isFetching && !isPending.true if the data in the cache is invalidated or if the data is older than the given staleTime.true if the data shown is placeholder data.true if the query failed while fetching for the first time.true if the query failed while refetching.The timestamp for when the query most recently returned the
status as 'success'.The timestamp for when the query most recently returned the
status as 'error'.The failure count for the query. Incremented every time the query fails. Reset to
0 when the query succeeds.The failure reason for the query retry. Reset to
null when the query succeeds.A function to manually refetch the query.
refetch({ cancelRefetch: true })
A stable promise that will resolve with the data of the query. Requires
experimental_prefetchInRender to be enabled.Examples
Basic Usage
import { useQuery } from '@tanstack/react-query'
function Todos() {
const { data, isLoading, error } = useQuery({
queryKey: ['todos'],
queryFn: async () => {
const response = await fetch('/api/todos')
if (!response.ok) throw new Error('Network response was not ok')
return response.json()
},
})
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
)
}
With Query Key Variables
function Todo({ todoId }: { todoId: number }) {
const { data } = useQuery({
queryKey: ['todo', todoId],
queryFn: async ({ queryKey }) => {
const [, id] = queryKey
const response = await fetch(`/api/todos/${id}`)
return response.json()
},
})
return <div>{data?.title}</div>
}
Dependent Query
function UserProjects({ userId }: { userId: number | null }) {
const { data } = useQuery({
queryKey: ['projects', userId],
queryFn: ({ queryKey }) => fetchProjects(queryKey[1]),
enabled: !!userId, // Only run when userId is available
})
// ...
}
With Select Transform
const { data } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
select: data => data.filter(todo => todo.completed),
})
With Type Safety
interface Todo {
id: number
title: string
completed: boolean
}
const { data } = useQuery<Todo[], Error>({
queryKey: ['todos'],
queryFn: fetchTodos,
})
// data is typed as Todo[] | undefined
// error is typed as Error | null
Notes
The query will automatically execute when the component mounts and whenever the
queryKey changes, unless enabled is set to false.If
initialData is provided, the query will return status: 'success' immediately and data will be defined.For queries with
suspense: true, use useSuspenseQuery instead for better type safety.