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.