Skip to main content

useIsFetching

The useIsFetching hook returns the number of queries that are currently fetching. This is useful for showing a global loading indicator.

Import

import { useIsFetching } from '@tanstack/react-query'

Signature

function useIsFetching(
  filters?: QueryFilters,
  queryClient?: QueryClient,
): number

Parameters

filters
QueryFilters
Optional filters to narrow down which queries to count
queryKey
QueryKey
Filter by query key
exact
boolean
If true, only match queries with the exact query key
predicate
(query: Query) => boolean
Custom predicate function to filter queries
type
'active' | 'inactive' | 'all'
Filter by query type
stale
boolean
Filter by stale state
queryClient
QueryClient
Optional QueryClient instance. If not provided, uses the context client.

Returns

count
number
The number of queries currently fetching that match the filters

Examples

Global Loading Indicator

import { useIsFetching } from '@tanstack/react-query'

function GlobalLoadingIndicator() {
  const isFetching = useIsFetching()

  return isFetching ? (
    <div className="loading-spinner">
      Loading...
    </div>
  ) : null
}

In App Layout

import { useIsFetching } from '@tanstack/react-query'

function AppLayout({ children }) {
  const isFetching = useIsFetching()

  return (
    <div>
      <header>
        <h1>My App</h1>
        {isFetching > 0 && (
          <div className="progress-bar" />
        )}
      </header>
      <main>{children}</main>
    </div>
  )
}

With Specific Query Filter

import { useIsFetching } from '@tanstack/react-query'

function PostsSection() {
  // Only count queries with 'posts' in their key
  const isFetchingPosts = useIsFetching({ queryKey: ['posts'] })

  return (
    <div>
      <h2>Posts {isFetchingPosts > 0 && '(Loading...)'}</h2>
      <PostsList />
    </div>
  )
}

Multiple Indicators

import { useIsFetching } from '@tanstack/react-query'

function Dashboard() {
  const totalFetching = useIsFetching()
  const fetchingPosts = useIsFetching({ queryKey: ['posts'] })
  const fetchingUsers = useIsFetching({ queryKey: ['users'] })

  return (
    <div>
      <header>
        {totalFetching > 0 && <GlobalSpinner />}
      </header>
      
      <div>
        <h2>Posts {fetchingPosts > 0 && <Spinner />}</h2>
        <Posts />
      </div>
      
      <div>
        <h2>Users {fetchingUsers > 0 && <Spinner />}</h2>
        <Users />
      </div>
    </div>
  )
}

With Exact Matching

import { useIsFetching } from '@tanstack/react-query'

function Component() {
  // Only count queries with exact key ['posts', 'list']
  const isFetching = useIsFetching({
    queryKey: ['posts', 'list'],
    exact: true,
  })

  return <div>{isFetching > 0 ? 'Loading posts...' : 'Idle'}</div>
}

With Custom Predicate

import { useIsFetching } from '@tanstack/react-query'

function Component() {
  // Count only stale queries that are fetching
  const isFetchingStale = useIsFetching({
    predicate: (query) => query.state.status === 'pending' && query.isStale(),
  })

  return (
    <div>
      {isFetchingStale > 0 && (
        <p>Refreshing {isFetchingStale} stale queries...</p>
      )}
    </div>
  )
}

Showing Count

import { useIsFetching } from '@tanstack/react-query'

function LoadingBadge() {
  const count = useIsFetching()

  if (count === 0) return null

  return (
    <div className="loading-badge">
      {count} {count === 1 ? 'request' : 'requests'} in progress
    </div>
  )
}

Combined with State

import { useState, useEffect } from 'react'
import { useIsFetching } from '@tanstack/react-query'

function Component() {
  const isFetching = useIsFetching()
  const [hasShownLoading, setHasShownLoading] = useState(false)

  useEffect(() => {
    if (isFetching > 0) {
      setHasShownLoading(true)
    }
  }, [isFetching])

  return (
    <div>
      {isFetching > 0 && <Spinner />}
      {!isFetching && hasShownLoading && <SuccessMessage />}
    </div>
  )
}

Delayed Indicator

import { useState, useEffect } from 'react'
import { useIsFetching } from '@tanstack/react-query'

function DelayedLoadingIndicator() {
  const isFetching = useIsFetching()
  const [showLoading, setShowLoading] = useState(false)

  useEffect(() => {
    if (isFetching > 0) {
      // Only show indicator if fetching takes longer than 500ms
      const timer = setTimeout(() => setShowLoading(true), 500)
      return () => clearTimeout(timer)
    } else {
      setShowLoading(false)
    }
  }, [isFetching])

  return showLoading ? <LoadingSpinner /> : null
}

Filter by Type

import { useIsFetching } from '@tanstack/react-query'

function Component() {
  const activeFetching = useIsFetching({ type: 'active' })
  
  return (
    <div>
      {activeFetching > 0 ? 'Active queries loading...' : 'No active queries'}
    </div>
  )
}

Notes

  • The hook uses useSyncExternalStore to efficiently subscribe to query cache changes
  • Returns 0 when no queries are fetching
  • Updates automatically when queries start or stop fetching
  • Can be used multiple times with different filters in the same component
  • Useful for implementing global or section-specific loading indicators
  • The count includes all queries that match the filters, including background refetches
  • Does not cause re-renders unless the count actually changes

Build docs developers (and LLMs) love