Hooks for managing, caching and syncing asynchronous and remote data in Vue
—
Core composables for fetching and caching data with reactive state management, automatic background refetching, cache invalidation, and optimistic updates.
Main composable for data fetching with intelligent caching and background synchronization.
/**
* Main composable for data fetching with intelligent caching
* @param options - Query configuration options with Vue reactivity support
* @param queryClient - Optional query client instance
* @returns Reactive query state and utilities
*/
function useQuery<TQueryFnData, TError, TData, TQueryKey>(
options: UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
queryClient?: QueryClient
): UseQueryReturnType<TData, TError>;
// Overload for queries with defined initial data
function useQuery<TQueryFnData, TError, TData, TQueryKey>(
options: DefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey>,
queryClient?: QueryClient
): UseQueryDefinedReturnType<TData, TError>;
interface UseQueryOptions<TQueryFnData, TError, TData, TQueryKey> {
queryKey: MaybeRefOrGetter<TQueryKey>;
queryFn?: MaybeRefOrGetter<QueryFunction<TQueryFnData, TQueryKey>>;
enabled?: MaybeRefOrGetter<boolean>;
staleTime?: MaybeRefOrGetter<number>;
gcTime?: MaybeRefOrGetter<number>;
refetchInterval?: MaybeRefOrGetter<number | false>;
refetchIntervalInBackground?: MaybeRefOrGetter<boolean>;
refetchOnMount?: MaybeRefOrGetter<boolean | "always">;
refetchOnWindowFocus?: MaybeRefOrGetter<boolean | "always">;
refetchOnReconnect?: MaybeRefOrGetter<boolean | "always">;
retry?: MaybeRefOrGetter<boolean | number | ((failureCount: number, error: TError) => boolean)>;
retryDelay?: MaybeRefOrGetter<number | ((retryAttempt: number, error: TError) => number)>;
select?: MaybeRefOrGetter<(data: TQueryFnData) => TData>;
placeholderData?: MaybeRefOrGetter<TData | PlaceholderDataFunction<TData>>;
initialData?: MaybeRefOrGetter<TData | InitialDataFunction<TData>>;
initialDataUpdatedAt?: MaybeRefOrGetter<number | (() => number | undefined)>;
throwOnError?: MaybeRefOrGetter<boolean | ((error: TError) => boolean)>;
meta?: MaybeRefOrGetter<QueryMeta>;
notifyOnChangeProps?: MaybeRefOrGetter<Array<keyof UseQueryReturnType<TData, TError>>>;
shallow?: boolean;
}
interface UseQueryReturnType<TData, TError> {
data: Ref<TData | undefined>;
dataUpdatedAt: Ref<number>;
error: Ref<TError | null>;
errorUpdatedAt: Ref<number>;
failureCount: Ref<number>;
failureReason: Ref<TError | null>;
fetchStatus: Ref<FetchStatus>;
isError: Ref<boolean>;
isFetched: Ref<boolean>;
isFetchedAfterMount: Ref<boolean>;
isFetching: Ref<boolean>;
isInitialLoading: Ref<boolean>;
isLoading: Ref<boolean>;
isLoadingError: Ref<boolean>;
isPaused: Ref<boolean>;
isPending: Ref<boolean>;
isPlaceholderData: Ref<boolean>;
isRefetchError: Ref<boolean>;
isRefetching: Ref<boolean>;
isStale: Ref<boolean>;
isSuccess: Ref<boolean>;
refetch: (options?: RefetchOptions) => Promise<QueryObserverResult<TData, TError>>;
status: Ref<QueryStatus>;
suspense: () => Promise<UseQueryReturnType<TData, TError>>;
}Usage Examples:
import { useQuery, keepPreviousData } from '@tanstack/vue-query';
// Basic query
const { data, isLoading, error } = useQuery({
queryKey: ['todos'],
queryFn: () => fetch('/api/todos').then(res => res.json())
});
// Query with reactive parameters
const userId = ref(1);
const { data: user } = useQuery({
queryKey: () => ['user', userId.value],
queryFn: ({ queryKey }) => fetch(`/api/users/${queryKey[1]}`).then(res => res.json()),
enabled: () => !!userId.value
});
// Query with data transformation
const { data: processedData } = useQuery({
queryKey: ['dashboard'],
queryFn: () => fetch('/api/dashboard').then(res => res.json()),
select: (data) => ({
summary: data.metrics.reduce((acc, m) => acc + m.value, 0),
items: data.items.filter(item => item.active)
})
});
// Query with placeholder data
const { data } = useQuery({
queryKey: ['posts', page],
queryFn: ({ queryKey }) => fetch(`/api/posts?page=${queryKey[1]}`).then(res => res.json()),
placeholderData: keepPreviousData
});Composable for paginated data fetching with automatic page management and infinite scrolling support.
/**
* Composable for paginated data fetching with infinite scrolling
* @param options - Infinite query configuration options
* @param queryClient - Optional query client instance
* @returns Reactive infinite query state with page management
*/
function useInfiniteQuery<TQueryFnData, TError, TData, TQueryKey, TPageParam>(
options: UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>,
queryClient?: QueryClient
): UseInfiniteQueryReturnType<TData, TError>;
interface UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>
extends Omit<UseQueryOptions<TQueryFnData, TError, TData, TQueryKey>, 'queryFn'> {
queryFn: MaybeRefOrGetter<
(context: QueryFunctionContext<TQueryKey, TPageParam>) => TQueryFnData | Promise<TQueryFnData>
>;
initialPageParam: MaybeRefOrGetter<TPageParam>;
getNextPageParam: MaybeRefOrGetter<
(lastPage: TQueryFnData, allPages: TQueryFnData[], lastPageParam: TPageParam, allPageParams: TPageParam[]) => TPageParam | undefined | null
>;
getPreviousPageParam?: MaybeRefOrGetter<
(firstPage: TQueryFnData, allPages: TQueryFnData[], firstPageParam: TPageParam, allPageParams: TPageParam[]) => TPageParam | undefined | null
>;
maxPages?: MaybeRefOrGetter<number>;
}
interface UseInfiniteQueryReturnType<TData, TError> extends UseQueryReturnType<TData, TError> {
data: Ref<InfiniteData<TData> | undefined>;
fetchNextPage: (options?: FetchNextPageOptions) => Promise<InfiniteQueryObserverResult<TData, TError>>;
fetchPreviousPage: (options?: FetchPreviousPageOptions) => Promise<InfiniteQueryObserverResult<TData, TError>>;
hasNextPage: Ref<boolean>;
hasPreviousPage: Ref<boolean>;
isFetchingNextPage: Ref<boolean>;
isFetchingPreviousPage: Ref<boolean>;
}
interface InfiniteData<TData, TPageParam = unknown> {
pages: TData[];
pageParams: TPageParam[];
}Usage Examples:
import { useInfiniteQuery } from '@tanstack/vue-query';
// Basic infinite query
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage
} = useInfiniteQuery({
queryKey: ['posts'],
queryFn: ({ pageParam = 1 }) =>
fetch(`/api/posts?page=${pageParam}`).then(res => res.json()),
initialPageParam: 1,
getNextPageParam: (lastPage, allPages) =>
lastPage.hasNextPage ? allPages.length + 1 : undefined
});
// Infinite query with search
const searchTerm = ref('');
const {
data: searchResults,
fetchNextPage: loadMore,
hasNextPage,
isFetchingNextPage: isLoadingMore
} = useInfiniteQuery({
queryKey: () => ['search', searchTerm.value],
queryFn: ({ queryKey, pageParam = 0 }) =>
fetch(`/api/search?q=${queryKey[1]}&offset=${pageParam}`)
.then(res => res.json()),
initialPageParam: 0,
getNextPageParam: (lastPage, allPages, lastPageParam) =>
lastPage.results.length === 20 ? lastPageParam + 20 : undefined,
enabled: () => searchTerm.value.length > 0
});
// Access flattened data
const allPosts = computed(() =>
data.value?.pages.flatMap(page => page.posts) ?? []
);Composable for running multiple queries in parallel with individual state management.
/**
* Run multiple queries in parallel with individual state management
* @param options - Configuration with array of query options and optional combine function
* @param queryClient - Optional query client instance
* @returns Readonly ref containing array of query results
*/
function useQueries<T extends Array<any>, TCombinedResult = UseQueriesResults<T>>(
options: {
queries: MaybeRefOrGetter<UseQueriesOptions<T>>;
combine?: (results: UseQueriesResults<T>) => TCombinedResult;
shallow?: boolean;
},
queryClient?: QueryClient
): Readonly<Ref<TCombinedResult>>;
type UseQueriesOptions<T extends Array<any>> = readonly [...{
[K in keyof T]: UseQueryOptions<any, any, any, any>
}];
type UseQueriesResults<T extends Array<any>> = {
[K in keyof T]: UseQueryReturnType<any, any>
};Usage Examples:
import { useQueries } from '@tanstack/vue-query';
// Multiple static queries
const queries = useQueries({
queries: [
{
queryKey: ['user'],
queryFn: () => fetch('/api/user').then(res => res.json())
},
{
queryKey: ['posts'],
queryFn: () => fetch('/api/posts').then(res => res.json())
},
{
queryKey: ['notifications'],
queryFn: () => fetch('/api/notifications').then(res => res.json())
}
]
});
// Access individual query results
const userQuery = computed(() => queries.value[0]);
const postsQuery = computed(() => queries.value[1]);
const notificationsQuery = computed(() => queries.value[2]);
// Dynamic queries based on reactive data
const userIds = ref([1, 2, 3]);
const userQueries = useQueries({
queries: () => userIds.value.map(id => ({
queryKey: ['user', id],
queryFn: () => fetch(`/api/users/${id}`).then(res => res.json())
}))
});
// Combined result processing
const summaryQuery = useQueries({
queries: [
{ queryKey: ['sales'], queryFn: () => fetchSales() },
{ queryKey: ['inventory'], queryFn: () => fetchInventory() },
{ queryKey: ['orders'], queryFn: () => fetchOrders() }
],
combine: (results) => ({
isLoading: results.some(query => query.isLoading),
hasError: results.some(query => query.isError),
data: {
sales: results[0].data,
inventory: results[1].data,
orders: results[2].data
}
})
});// Query option variants
interface DefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey>
extends UseQueryOptions<TQueryFnData, TError, TData, TQueryKey> {
initialData: TQueryFnData | (() => TQueryFnData);
}
interface UndefinedInitialQueryOptions<TQueryFnData, TError, TData, TQueryKey>
extends UseQueryOptions<TQueryFnData, TError, TData, TQueryKey> {
initialData?: undefined | (() => undefined);
}
// Return type variants
interface UseQueryDefinedReturnType<TData, TError> extends UseQueryReturnType<TData, TError> {
data: Ref<TData>; // Never undefined when initial data is defined
}
// Infinite query specific types
interface DefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>
extends UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> {
initialData:
| InfiniteData<TQueryFnData, TPageParam>
| (() => InfiniteData<TQueryFnData, TPageParam>);
}
interface UndefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>
extends UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam> {
initialData?: undefined;
}
// Page parameter function types
type GetNextPageParamFunction<TQueryFnData, TPageParam> = (
lastPage: TQueryFnData,
allPages: TQueryFnData[],
lastPageParam: TPageParam,
allPageParams: TPageParam[]
) => TPageParam | undefined | null;
type GetPreviousPageParamFunction<TQueryFnData, TPageParam> = (
firstPage: TQueryFnData,
allPages: TQueryFnData[],
firstPageParam: TPageParam,
allPageParams: TPageParam[]
) => TPageParam | undefined | null;
// Fetch options
interface FetchNextPageOptions {
cancelRefetch?: boolean;
}
interface FetchPreviousPageOptions {
cancelRefetch?: boolean;
}Install with Tessl CLI
npx tessl i tessl/npm-tanstack--vue-query