Command Palette

Search for a command to run...

Command Palette

Search for a command to run...

Blog
Next

Understanding loading states in Tanstack Query v5

A guide to isLoading, isPending, and isFetching in Tanstack Query v5

When using TanStack Query v5, you'll often see three flags: isPending, isFetching, and isLoading. They may look similar at first, but each one represents a different stage of the data-fetching process.

This guide explains what each flag means and how to use them effectively in your UI—with practical React examples.

Why these flags are important

TanStack Query handles data fetching, caching, and updates for you. However, your UI still needs to react to what's happening behind the scenes.

These flags help you understand the query's current state so you can display the right loading indicators at the right time.

isPending

isPending is true only during the very first fetch, when there is no data available yet.

Once data is successfully loaded, isPending becomes false—even if the data later becomes stale and is refetched.

When to use it: Use isPending when you want to show a full loading state, like a page loader or skeleton screen.

Example

import { useQuery } from "@tanstack/react-query"
 
function fetchUsers() {
  return fetch("/api/users").then((res) => res.json())
}
 
export default function Users() {
  const { data, isPending, error } = useQuery({
    queryKey: ["users"],
    queryFn: fetchUsers,
  })
 
  if (isPending) {
    return <div>Loading users...</div>
  }
 
  if (error) {
    return <div>Something went wrong</div>
  }
 
  return (
    <ul>
      {data.map((user) => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  )
}

isFetching

isFetching is true whenever a request is in progress, including:

  • the initial fetch
  • background refetches

This makes it ideal for showing subtle, non-blocking loading indicators.

When to use it: Use isFetching to show a small spinner or “refreshing…” indicator while keeping the current UI visible.

Example

import { useQuery } from "@tanstack/react-query"
 
function fetchPosts() {
  return fetch("/api/posts").then((res) => res.json())
}
 
export default function Posts() {
  const { data, isFetching, isPending } = useQuery({
    queryKey: ["posts"],
    queryFn: fetchPosts,
    refetchInterval: 5000,
  })
 
  if (isPending) {
    return <div>Loading posts...</div>
  }
 
  return (
    <div>
      {isFetching && <small>Refreshing...</small>}
 
      <ul>
        {data.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  )
}

isLoading

In TanStack Query v5, isLoading is a combination of isPending and isFetching.

It is true only when:

  • there is no cached data
  • and a fetch is currently happening

Because of this, it's often simpler to use isPending and isFetching directly.

When to use it: Use isLoading if you want a single flag for the initial loading state.

Example

import { useQuery } from "@tanstack/react-query"
 
function fetchTodos() {
  return fetch("/api/todos").then((res) => res.json())
}
 
export default function Todos() {
  const { data, isLoading, error } = useQuery({
    queryKey: ["todos"],
    queryFn: fetchTodos,
  })
 
  if (isLoading) {
    return <div>Loading todos...</div>
  }
 
  if (error) {
    return <div>Error loading todos</div>
  }
 
  return (
    <ul>
      {data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )
}

Summary

  • Use isPending → for first-time loading states (full-page loaders)
  • Use isFetching → for background updates (small indicators)
  • Use isLoading → if you want a combined, simpler flag

TanStack Query handles the heavy lifting—you just need to show the right UI at the right time.