The useQueries hook allows you to fetch multiple queries in parallel with a dynamic number of queries. It’s useful when you need to fetch an array of queries where the length is not known at build time.
Import
import { useQueries } from '@tanstack/react-query'
Signature
function useQueries < T extends Array < any >, TCombinedResult = QueriesResults < T >>({
queries ,
combine ,
} : {
queries : readonly [ ... QueriesOptions < T >]
combine ?: ( result : QueriesResults < T >) => TCombinedResult
subscribed ?: boolean
}, queryClient ?: QueryClient ) : TCombinedResult
Parameters
The queries options object. queries
readonly UseQueryOptions[]
required
An array of query option objects. Each element has the same options as useQuery. queries : [
{ queryKey: [ 'todo' , 1 ], queryFn : () => fetchTodo ( 1 ) },
{ queryKey: [ 'todo' , 2 ], queryFn : () => fetchTodo ( 2 ) },
]
combine
(result: UseQueryResult[]) => TCombinedResult
Optional function to combine or transform the results array. combine : ( results ) => ({
data: results . map ( r => r . data ),
pending: results . some ( r => r . isPending ),
})
Set to false to unsubscribe this observer from updates to the query cache.
Override the default QueryClient.
Query Options
Each query in the queries array accepts the following options:
The unique key for the query.
queryFn
QueryFunction<TQueryFnData, TQueryKey>
required
The function to fetch the data.
Set to false to disable the query.
staleTime
number | 'static'
default: "0"
Time in milliseconds after data is considered stale.
Garbage collection time in milliseconds.
retry
boolean | number | ((failureCount: number, error: TError) => boolean)
default: "3"
Retry configuration.
select
(data: TQueryData) => TData
Transform or select part of the data.
All options available to useQuery are also available for each query in the array, except placeholderData which has a slightly different signature.
Returns
By default, returns an array of query results. Each element corresponds to a query in the input array. The error for this query.
status
'pending' | 'error' | 'success'
The status of this query.
Whether this query is pending.
Whether this query is loading.
Whether this query is fetching.
Whether this query succeeded.
Whether this query errored.
refetch
() => Promise<UseQueryResult>
Function to refetch this query.
When using the combine option, returns the result of the combine function.
Examples
Basic Usage
import { useQueries } from '@tanstack/react-query'
function TodosComponent ({ todoIds } : { todoIds : number [] }) {
const results = useQueries ({
queries: todoIds . map ( id => ({
queryKey: [ 'todo' , id ],
queryFn : () => fetchTodo ( id ),
staleTime: 5000 ,
})),
})
return (
< div >
{ results . map (( result , index ) => (
< div key = {todoIds [index]}>
{result.isLoading && <div>Loading...</div>}
{result.isError && <div>Error: {result.error. message } </ div > }
{ result . isSuccess && < div >{ result . data . title }</ div >}
</ div >
))}
</ div >
)
}
With Type Safety
interface Todo {
id : number
title : string
completed : boolean
}
const todoIds = [ 1 , 2 , 3 ]
const results = useQueries ({
queries: todoIds . map ( id => ({
queryKey: [ 'todo' , id ] as const ,
queryFn : async () : Promise < Todo > => {
const response = await fetch ( `/api/todos/ ${ id } ` )
return response . json ()
},
})),
})
// results is typed as UseQueryResult<Todo, Error>[]
Using Combine
const combinedResult = useQueries ({
queries: todoIds . map ( id => ({
queryKey: [ 'todo' , id ],
queryFn : () => fetchTodo ( id ),
})),
combine : ( results ) => {
return {
data: results . map ( result => result . data ),
isPending: results . some ( result => result . isPending ),
isError: results . some ( result => result . isError ),
}
},
})
// combinedResult.data is (Todo | undefined)[]
// combinedResult.isPending is boolean
// combinedResult.isError is boolean
Dynamic Queries with Conditional Execution
const results = useQueries ({
queries: userIds . map ( userId => ({
queryKey: [ 'user' , userId ],
queryFn : () => fetchUser ( userId ),
enabled: !! userId , // Only fetch if userId exists
})),
})
Handling All States
const results = useQueries ({
queries: todoIds . map ( id => ({
queryKey: [ 'todo' , id ],
queryFn : () => fetchTodo ( id ),
})),
})
const isLoading = results . some ( result => result . isLoading )
const isError = results . some ( result => result . isError )
const errors = results . filter ( result => result . isError ). map ( result => result . error )
const allData = results . map ( result => result . data )
if ( isLoading ) return < div > Loading todos ...</ div >
if ( isError ) return < div > Some todos failed to load </ div >
return (
< ul >
{ allData . map (( todo , index ) => (
< li key = {todoIds [index]}>{todo?.title}</li>
))}
</ul>
)
const results = useQueries ({
queries: todoIds . map ( id => ({
queryKey: [ 'todo' , id ],
queryFn : () => fetchTodo ( id ),
select : ( todo : Todo ) => todo . title , // Only select the title
})),
})
// Each result.data is now just the title string
Combining with Other Data
interface CombinedData {
todos : Todo []
completedCount : number
pendingCount : number
hasErrors : boolean
}
const combined = useQueries ({
queries: todoIds . map ( id => ({
queryKey: [ 'todo' , id ],
queryFn : () => fetchTodo ( id ),
})),
combine : ( results ) : CombinedData => {
const todos = results
. filter ( r => r . data )
. map ( r => r . data ! )
return {
todos ,
completedCount: todos . filter ( t => t . completed ). length ,
pendingCount: todos . filter ( t => ! t . completed ). length ,
hasErrors: results . some ( r => r . isError ),
}
},
})
// combined.todos, combined.completedCount, etc.
Notes
The queries array must be a stable reference (e.g., use useMemo if generating dynamically) or the hook will re-render infinitely.
Use useQueries when the number of queries is dynamic. If you have a fixed number of queries, use multiple useQuery calls instead.
Each query in the array is tracked independently, so you can have different loading states, errors, and data for each query.
The combine option is useful for deriving computed values from multiple queries or reducing the number of re-renders.