Skip to main content
Execute multiple queries in parallel with the useQueries composable. It returns an array of query results that are all reactive and managed independently.

Signature

function useQueries<T extends Array<any>, TCombinedResult>(
  options: {
    queries: UseQueriesOptionsArg<T> | (() => UseQueriesOptionsArg<T>)
    combine?: (results: UseQueriesResults<T>) => TCombinedResult
  } & ShallowOption,
  queryClient?: QueryClient,
): Readonly<Ref<TCombinedResult>>

Parameters

options
object
required
Configuration object for multiple queries.
queryClient
QueryClient
Custom QueryClient instance. If not provided, uses the client from context.

Returns

Readonly<Ref<TCombinedResult>>
Ref
Reactive ref containing array of query results (or combined result if combine is provided).When not using combine, each element contains:

Type Parameters

  • T - Tuple type representing the array of query options
  • TCombinedResult - Type of combined result (defaults to UseQueriesResults<T>)

Examples

Basic Usage

<script setup>
import { ref } from 'vue'
import { useQueries } from '@tanstack/vue-query'

const userIds = ref([1, 2, 3])

const results = useQueries({
  queries: userIds.value.map((id) => ({
    queryKey: ['user', id],
    queryFn: () => fetchUser(id),
  })),
})

// results.value is an array of query results
</script>

<template>
  <div>
    <div v-for="(result, index) in results" :key="index">
      <div v-if="result.isLoading">Loading user {{ index + 1 }}...</div>
      <div v-else-if="result.error">Error: {{ result.error.message }}</div>
      <div v-else>{{ result.data?.name }}</div>
    </div>
  </div>
</template>

With TypeScript

<script setup lang="ts">
import { useQueries } from '@tanstack/vue-query'

interface User {
  id: number
  name: string
  email: string
}

interface Post {
  id: number
  title: string
  userId: number
}

const results = useQueries({
  queries: [
    {
      queryKey: ['user', 1],
      queryFn: async (): Promise<User> => {
        const res = await fetch('/api/users/1')
        return res.json()
      },
    },
    {
      queryKey: ['posts'],
      queryFn: async (): Promise<Post[]> => {
        const res = await fetch('/api/posts')
        return res.json()
      },
    },
  ],
})

// results.value[0].data is typed as User | undefined
// results.value[1].data is typed as Post[] | undefined
</script>

Dynamic Queries

<script setup>
import { ref, computed } from 'vue'
import { useQueries } from '@tanstack/vue-query'

const userIds = ref([1, 2, 3])

// Use a function to make queries reactive
const results = useQueries({
  queries: () => userIds.value.map((id) => ({
    queryKey: ['user', id],
    queryFn: () => fetchUser(id),
  })),
})

const addUser = () => {
  userIds.value.push(userIds.value.length + 1)
  // Automatically triggers new query for the new user
}
</script>

Combining Results

<script setup>
import { useQueries } from '@tanstack/vue-query'

const combinedData = useQueries({
  queries: [
    { queryKey: ['users'], queryFn: fetchUsers },
    { queryKey: ['posts'], queryFn: fetchPosts },
    { queryKey: ['comments'], queryFn: fetchComments },
  ],
  combine: (results) => {
    return {
      users: results[0].data,
      posts: results[1].data,
      comments: results[2].data,
      isLoading: results.some(r => r.isLoading),
      isError: results.some(r => r.isError),
    }
  },
})

// combinedData.value has the structure defined in combine function
</script>

<template>
  <div>
    <div v-if="combinedData.isLoading">Loading...</div>
    <div v-else-if="combinedData.isError">Error occurred</div>
    <div v-else>
      <p>Users: {{ combinedData.users?.length }}</p>
      <p>Posts: {{ combinedData.posts?.length }}</p>
      <p>Comments: {{ combinedData.comments?.length }}</p>
    </div>
  </div>
</template>

Conditional Queries

<script setup>
import { ref } from 'vue'
import { useQueries } from '@tanstack/vue-query'

const userIds = ref([1, 2, 3])
const enabled = ref(true)

const results = useQueries({
  queries: () => userIds.value.map((id) => ({
    queryKey: ['user', id],
    queryFn: () => fetchUser(id),
    enabled: enabled.value, // All queries controlled by same flag
  })),
})
</script>

Different Query Options

<script setup>
import { useQueries } from '@tanstack/vue-query'

const results = useQueries({
  queries: [
    {
      queryKey: ['critical-data'],
      queryFn: fetchCriticalData,
      staleTime: 0, // Always fresh
      refetchOnWindowFocus: true,
    },
    {
      queryKey: ['cached-data'],
      queryFn: fetchCachedData,
      staleTime: 1000 * 60 * 5, // 5 minutes
      gcTime: 1000 * 60 * 10, // 10 minutes
    },
    {
      queryKey: ['background-data'],
      queryFn: fetchBackgroundData,
      refetchInterval: 30000, // Refetch every 30 seconds
    },
  ],
})
</script>

Accessing Individual Results

<script setup>
import { computed } from 'vue'
import { useQueries } from '@tanstack/vue-query'

const results = useQueries({
  queries: [
    { queryKey: ['user'], queryFn: fetchUser },
    { queryKey: ['posts'], queryFn: fetchPosts },
  ],
})

// Extract individual results
const userQuery = computed(() => results.value[0])
const postsQuery = computed(() => results.value[1])

// Check if all queries are loaded
const allLoaded = computed(() => 
  results.value.every(r => r.isSuccess)
)

// Check if any query is loading
const anyLoading = computed(() => 
  results.value.some(r => r.isLoading)
)
</script>

Refetch Individual Queries

<script setup>
import { useQueries } from '@tanstack/vue-query'

const results = useQueries({
  queries: [
    { queryKey: ['user'], queryFn: fetchUser },
    { queryKey: ['posts'], queryFn: fetchPosts },
  ],
})

const refetchUser = () => {
  results.value[0].refetch()
}

const refetchAll = () => {
  results.value.forEach(result => result.refetch())
}
</script>

Type-Safe Queries Array

<script setup lang="ts">
import { useQueries } from '@tanstack/vue-query'

interface User {
  id: number
  name: string
}

interface Post {
  id: number
  title: string
}

// Explicitly type the queries array
const results = useQueries({
  queries: [
    {
      queryKey: ['user'] as const,
      queryFn: async (): Promise<User> => {
        const res = await fetch('/api/user')
        return res.json()
      },
    },
    {
      queryKey: ['posts'] as const,
      queryFn: async (): Promise<Post[]> => {
        const res = await fetch('/api/posts')
        return res.json()
      },
    },
  ] as const,
})

// Full type inference for each result
</script>

Notes

Each query in the array is tracked independently. Changing one query’s parameters will only refetch that specific query.
Use the combine option to derive custom data structures from the results array, such as aggregating loading states or merging data.
When using a function for the queries option, make sure it’s reactive (uses Vue refs/computed). Static arrays won’t react to changes.

Build docs developers (and LLMs) love