Skip to main content

useQueries

The useQueries hook allows you to fetch multiple queries in parallel. It accepts an array of query options objects and returns an array of query results.

Import

import { useQueries } from '@tanstack/react-query'

Signature

function useQueries<T extends Array<any>, TCombinedResult = QueriesResults<T>>({
  queries,
  combine,
  subscribed,
}: {
  queries: readonly [...QueriesOptions<T>]
  combine?: (result: QueriesResults<T>) => TCombinedResult
  subscribed?: boolean
}, queryClient?: QueryClient): TCombinedResult

Type Parameters

T
type
Array type containing the configuration for each query
TCombinedResult
type
default:"QueriesResults<T>"
The type of the combined result after the combine function is applied

Parameters

options
object
required
queries
readonly [...QueriesOptions<T>]
required
An array of query option objects. Each query option object accepts most useQuery options except:
  • placeholderData cannot be a function
  • subscribed is not available per query (use the top-level subscribed instead)
Each query can have:
  • queryKey (required): Unique key for the query
  • queryFn: Function that fetches the data
  • enabled: Whether the query should run
  • staleTime: Time in ms after data is considered stale
  • gcTime: Time in ms for garbage collection
  • select: Transform function for the data
  • All other standard useQuery options
combine
(result: QueriesResults<T>) => TCombinedResult
Optional function to combine or transform the array of query results into a different shape
subscribed
boolean
default:"true"
Set this to false to unsubscribe all queries from updates to the query cache
queryClient
QueryClient
Optional QueryClient instance to use. If not provided, the client from the nearest QueryClientProvider will be used.

Returns

Returns an array of query results, where each result has the same shape as the result from useQuery:
Array<{
  data: TData
  error: TError | null
  status: 'pending' | 'error' | 'success'
  fetchStatus: 'fetching' | 'paused' | 'idle'
  isPending: boolean
  isSuccess: boolean
  isError: boolean
  isFetching: boolean
  // ... all other useQuery result properties
}>
If a combine function is provided, returns the result of that function instead.

Type Inference

The useQueries hook provides excellent type inference:
// Explicit type parameters
const results = useQueries({
  queries: [
    { queryKey: ['user'], queryFn: fetchUser },
    { queryKey: ['posts'], queryFn: fetchPosts },
  ]
})
// results[0].data is inferred from fetchUser return type
// results[1].data is inferred from fetchPosts return type

// Using type hints for better inference
const results = useQueries({
  queries: [
    {
      queryKey: ['user'],
      queryFn: fetchUser,
    } as const,
    {
      queryKey: ['posts'],
      queryFn: fetchPosts,
    } as const,
  ]
})

Examples

Basic Usage

import { useQueries } from '@tanstack/react-query'

function UserData({ userIds }: { userIds: number[] }) {
  const results = useQueries({
    queries: userIds.map((id) => ({
      queryKey: ['user', id],
      queryFn: async () => {
        const response = await fetch(`/api/users/${id}`)
        return response.json()
      },
      staleTime: 1000 * 60 * 5,
    })),
  })

  const isLoading = results.some((result) => result.isPending)
  const isError = results.some((result) => result.isError)

  if (isLoading) return <div>Loading...</div>
  if (isError) return <div>Error loading users</div>

  return (
    <div>
      {results.map((result, i) => (
        <div key={userIds[i]}>
          {result.data?.name}
        </div>
      ))}
    </div>
  )
}

With Type Safety

interface User {
  id: number
  name: string
}

interface Post {
  id: number
  title: string
}

function Dashboard() {
  const [userQuery, postsQuery] = useQueries({
    queries: [
      {
        queryKey: ['user'],
        queryFn: async (): Promise<User> => {
          const response = await fetch('/api/user')
          return response.json()
        },
      },
      {
        queryKey: ['posts'],
        queryFn: async (): Promise<Post[]> => {
          const response = await fetch('/api/posts')
          return response.json()
        },
      },
    ],
  })

  // userQuery.data is typed as User | undefined
  // postsQuery.data is typed as Post[] | undefined
}

Dynamic Queries

function UserPosts({ userId, postIds }: { userId: number; postIds: number[] }) {
  const queries = useQueries({
    queries: [
      {
        queryKey: ['user', userId],
        queryFn: () => fetchUser(userId),
      },
      ...postIds.map((postId) => ({
        queryKey: ['post', postId],
        queryFn: () => fetchPost(postId),
      })),
    ],
  })

  const [userQuery, ...postQueries] = queries
}

With Combine Function

function Dashboard() {
  const combinedData = useQueries({
    queries: [
      { queryKey: ['user'], queryFn: fetchUser },
      { queryKey: ['posts'], queryFn: fetchPosts },
      { queryKey: ['comments'], queryFn: fetchComments },
    ],
    combine: (results) => {
      return {
        data: results.map((result) => result.data),
        isPending: results.some((result) => result.isPending),
        isError: results.some((result) => result.isError),
      }
    },
  })

  // combinedData has shape: { data: any[], isPending: boolean, isError: boolean }
}

Conditional Queries

function UserDetails({ userId, includeStats }: { userId: number; includeStats: boolean }) {
  const results = useQueries({
    queries: [
      {
        queryKey: ['user', userId],
        queryFn: () => fetchUser(userId),
      },
      {
        queryKey: ['user-stats', userId],
        queryFn: () => fetchUserStats(userId),
        enabled: includeStats, // Only fetch if includeStats is true
      },
    ],
  })

  const [userQuery, statsQuery] = results
}

Dependent Queries

function UserWithPosts({ userId }: { userId: number }) {
  const results = useQueries({
    queries: [
      {
        queryKey: ['user', userId],
        queryFn: () => fetchUser(userId),
      },
      {
        queryKey: ['posts', userId],
        queryFn: () => fetchUserPosts(userId),
        enabled: !!userId, // Only fetch posts if we have a userId
      },
    ],
  })

  const [userQuery, postsQuery] = results
}

With Error Handling

function MultiDataFetch() {
  const results = useQueries({
    queries: [
      {
        queryKey: ['data1'],
        queryFn: fetchData1,
        retry: 3,
      },
      {
        queryKey: ['data2'],
        queryFn: fetchData2,
        retry: 1,
      },
    ],
  })

  const errors = results
    .filter((result) => result.isError)
    .map((result) => result.error)

  if (errors.length > 0) {
    return (
      <div>
        {errors.map((error, i) => (
          <div key={i}>Error: {error.message}</div>
        ))}
      </div>
    )
  }

  return <div>All data loaded successfully</div>
}

With Type Inference from Query Options

const userQueries = [
  { queryKey: ['user', 1], queryFn: () => fetchUser(1) },
  { queryKey: ['user', 2], queryFn: () => fetchUser(2) },
] as const

function Users() {
  const results = useQueries({
    queries: userQueries,
  })

  // Type inference works based on query definitions
}

Important Notes

  • The queries are executed in parallel
  • Each query maintains its own cache entry
  • The combine function is called on every render, so keep it lightweight or memoize expensive computations
  • Unlike useQuery, you cannot use placeholderData as a function in useQueries
  • The order of results matches the order of queries in the input array
  • Supports Suspense and Error Boundaries when individual queries have those options enabled

Source

Implementation: useQueries.ts:207

Build docs developers (and LLMs) love