Because TanStack Query uses a global cache, you can use the same useQuery hook in multiple places without making excess XHR requests.

Traditionally, if you have data on a server, that you want to use in multiple places throughout your component tree, the advice is to lift it to the top and either prop-drill its data or put it in a context. In this contrived example, suppose the <Header> component and the <Dashboard> component both need the data from the primary server fetch. And the <Footer> component needs to know if there was an error fetching that server data:


function App() {
  const query = useQuery({
    queryKey: ["data"],
    queryFn: () => fetchData()
  })

  return <div>
    <Header data={query.data}/>
    <Dashboard data={query.data} isPending={query.isPending}/>
    <Footer error={query.error}/>
  </div>
}

But prop-drilling can be annoying. For example, that <Footer> component might look like this:


function Footer({error}: {error: Error | null}) {
  return <footer>
    <Alert error={error}/>
    <p>Copyright 2026</p>
  </footer>
}

function Alert({error}: {error: Error | null}) {
  if (error) {
    return <div className="footer-error">There was an error fetching the data</div>
  }
  return null
}

What you can do instead

First, refactor the useQuery call so it becomes a hook that encapsulates any of its configuration. For example:


function useData() {
  return useQuery({
    queryKey: ["data"],
    queryFn: () => fetchData(),
    retry: 0,
  });
}

Now, you can use that hook in all the places where it's needed. For example:


function Header() {
  const {data} = useData();
  return <h1>
    Page Title {data ? ` (${data.count} items)` : null}
  </h1>
}

function Footer() {
  return <footer>
    <Alert/>
    <p>Copyright 2026</p>
  </footer>
}

function Alert() {
  const {error} = useData()
  if (error) {
    return <div className="footer-error">There was an error fetching the data</div>
  }
  return null
}

And in the end, you can remove any drop-drilling


function App() {
  return <div>
    <Header/>
    <Dashboard/>
    <Footer/>
  </div>
}

Conclusion

The useQuery hook will use the global cache, that probably looks something like this:


import { App } from "./App";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

const queryClient = new QueryClient();

const app = (
  <StrictMode>
    <QueryClientProvider client={queryClient}>
      <App />
    </QueryClientProvider>
  </StrictMode>
);

And that global cache will recognize if a query has started by the queryKey value. If one such Promise already exists, it won't start another. So you're guaranteed to not make multiple XHR queries to the back end.

Comments

Your email will never ever be published.

Previous:
Autocomplete using PostgreSQL instead of Elasticsearch December 18, 2025 Python, PostgreSQL, Elasticsearch
Related by category:
Benchmarking oxlint vs biome December 12, 2025 TypeScript
Testing out vite 8 on SPA: Vite 8 is 5x faster December 6, 2025 TypeScript
In Python, you have to specify the type and not rely on inference October 10, 2025 TypeScript
Always run biome migrate after upgrading biome August 16, 2025 JavaScript
Related by keyword:
An ideal pattern to combine React Router with TanStack Query November 18, 2024 React, JavaScript
How to handle success and failure in @tanstack/react-query useQuery hook September 16, 2024 React, JavaScript
swr compared to @tanstack/react-query August 30, 2024 JavaScript
Displaying fetch() errors and unwanted responses in React February 6, 2019 Web development, React, JavaScript