Advanced Data Fetching in Vue w/ TanStack Query

In the Vue world, we don’t really have an opinionated way of how to handle advanced data fetching.

We mostly rely on libraries like Axios to handle the actual creation of the requests, but after that we are left to our own devices in terms of handling how to process that data. There have not been standards for processes beyond fetching, such as caching, synchronizing and updating server state, etc…

In my own job, I found that once things became more complicated, it was a constant hassle handling all of the possible needs of the application beyond a simple fetch.

Clearly this was not a problem only I was experiencing, and fortunately we now have an exciting tool to turn to that helps us handle more complex and nuanced data-fetching scenarios. The tool is called Vue Query, and in this tutorial we’ll look at what querying crises it helps us solve and explore a working demo of it in action.

The @tanstack/vue-query package offers a 1st-class API for using TanStack Query via Vue.

Fetching starts out simple, but then…

Consider a “simple” task from your product team: Display a list of users from the database.

At first, you may think that you could just:

  1. Drop in an Axios request
  2. Poll your server’s API for the users
  3. Present the users

Easy enough, right?

But what happens once you get past the MVP?

What if that “simple” poll eventually requires pagination, or infinite-scroll loading, or automatic refetching to avoid stale data, or caching, or how about setting the retrieved data as part of a global state that should be smart enough not to re-fetch unless it’s been a while since the last poll?

Now our simple data-fetching task just became a lot more work.

We’re starting to touch on the scenarios where Tanstack’s Query for Vue comes to the rescue.

According to the website’s overview, Vue Query helps us with:

  • Caching (possibly the hardest thing to do in programming)
  • Deduping multiple requests for the same data into a single request
  • Updating “out of date” data in the background
  • Knowing when data is “out of date”
  • Reflecting updates to data as quickly as possible
  • Performance optimizations like pagination and lazy-loading data
  • Managing memory and garbage-collection of server state
  • Memoizing query results with structural sharing

A very common approach to solve all of these and more is to use GraphQL and Apollo. But realistically, it’s not always possible to implement or rewrite your backend just to be able to use GraphQL in your frontend.

The beauty of Tanstack Query is that it is completely agnostic about how you interact with your API. The only thing it needs is for you to provide it with a function that returns a Promise. This function can internally be using Axios, fetch, or whatever other solution you currently already have in place. You don’t even need to refactor most of your code to use it.

At this point you may be wondering if Tanstack Query is hard to learn or difficult to use. I’m happy to share with you that its not! With a very gentle learning curve, opinionated but sensible defaults, and clear documentation I was able to go from getting my team’s buy-in to production in less than a month (granted we haven’t replaced every single piece of data fetching with it, but you also probably shouldn’t).

A Vue Query Demo

Let’s build a quick example to showcase how easy it is to get started with Tanstack Query in Vue! The whole example can be found in this code sandbox if you want to skip right to it.

First, we need to install the necessary dependencies. I’m going to assume that you already have a Vue project set up.

yarn add @tanstack/vue-query axios

// OR

npm install @tanstack/vue-query axios

Next, we’ll create a new composition function. I like to personally put these inside a composables features folder, but the placement is up to you. We’re going to call it useDogQuery.js.

For our demo, we’re using the API to fetch the image of a random dog through Tanstack Query, with the help of Axios to make the actual network call. You can feel free to do the same, or use any other means of fetching, such as JS fetch.

Tanstack Query exposes a useQuery composition function, which we are going to import and return from our own useDogQuery composable.

📄 useDogQuery.js

import { useQuery } from "@tanstack/vue-query";

export default () => {
  return useQuery({

The useQuery function takes an object configuration param, which has several properties we can set. Two of the most important ones are queryKey and queryFn.

The queryKey allows us to set the “key”, an array that defines the query. Any values that you put in this array will help us identify this query.

In our case, we will define it as ["dog", "random"], since it’s a query that calls the dog API for a random image.

The array can also contain numbers, booleans, and even other arrays and objects. You can even pass down reactive Vue ref and computed values, but that’s beyond the scope this quick example.

📄 useDogQuery.js

export default () => {
  return useQuery({
    queryKey: ["dog", "random"]

The second property, queryFn is a function that makes an API call, and returns a Promise. We will use Axios, which itself returns a Promise and will make the network call for us.

This is the function that Tanstack Query will call to make the network request every time it needs to fetch new data.

📄 useDogQuery.js

export default () => {
  return useQuery({
    queryKey: ["dog", "random"],
    queryFn: () =>"get", "")

Finally, we’ll add one more param, select. Axios wraps the response of network calls in an object containing a data property.

Normally you would check for when using the response from an Axios call. In this case, I want my composition function to return the data only, without the clutter of information from the request (you may actually want to return the whole thing in other scenarios), so I’m going to use the select property to tell Tanstack Query what part of the response data I want it to return to the user of this composable.

export default () => {
  return useQuery({
    queryKey: ["dog", "random"],
    queryFn: () =>"get", ""),
    select: (response) => // extract data from Axios response

We’re all set with the composable, let’s now jump to App.vue or your component of choice and import and use it to fetch some dog images!

📄 App.vue

  <img v-if="data" :src="data.message" />

import useDogQuery from "./composables/useDogQuery";

export default {
  setup() {
    const { data } = useDogQuery();

    return { data };

Notice that the useDogQuery composable is returning data. This comes directly from the useQuery composable that we imported on the previous step. Tanstack Query returns a lot of information from the composable, like error and status. It’s very important to take a dive into the documentation so that you can squeeze all the power out of this amazing library.

To test this out, serve the application and open it in the browser. A dog image should load, and if you navigate to another tab in your browser and come back, you should see a fresh dog image because Vue Query considered the data “stale” once you navigated away from the page, so it refetched the query.

This behavior can be fully configured by setting refetch times, invalidation timers, toggling off and on the refetching when the user navigates away, etc. This is studying the documentation to learn all of its features really pays off.

Certainly you can start to envision all the ways this tool can help with the heavy lifting of more advanced and nuanced fetching scenarios. I hope this introduction to Vue Query helps you save you and your team time and stress like it has mine.

By the way, if you are interested in going the route of using GraphQL and Apollo, check out my course here on Vue Mastery, co-created with Natalia Tepluhina: Querying with GraphQL.

In this article:

Dive Deeper into Vue today

Access our entire course library with a special discount.

Get Deal

Download the cheatsheets

Save time and energy with our cheat sheets.