Skip to content

useTanstackQueryStableQueryClient (JavaScript)

Language JavaScript (and super languages)
biome.json
{
"linter": {
"rules": {
"nursery": {
"useTanstackQueryStableQueryClient": "error"
}
}
}
}

Require the TanStack Query QueryClient to be created once, not on every render.

The QueryClient holds the cache that stores the results of your queries. A React component runs its function body again each time it renders. When the body creates the client with new QueryClient(), every render gets a new client with an empty cache, and the data that was already fetched is lost.

Create the client outside of the component, or store it with useState so that React keeps the same client across renders.

This rule checks new QueryClient() calls that are assigned to a variable inside a React component or a custom hook, when QueryClient is imported from @tanstack/react-query. A function counts as a component when its name starts with an uppercase letter, and as a hook when its name starts with use followed by an uppercase letter.

Async components are allowed to create a client, because an async Server Component only runs once on the server for each request.

The fix wraps the client in React.useState. It expects React to be available in the file.

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
function App() {
const queryClient = new QueryClient();
return (
<QueryClientProvider client={queryClient}>
<Home />
</QueryClientProvider>
);
}
code-block.jsx:4:25 lint/nursery/useTanstackQueryStableQueryClient  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━

✖ This QueryClient is created again each time the component renders.

3 │ function App() {
> 4 │ const queryClient = new QueryClient();
│ ^^^^^^^^^^^^^^^^^
5 │ return (
6 │ <QueryClientProvider client={queryClient}>

ℹ Each new client starts with an empty cache, so the data that was already fetched is lost on every render.

ℹ Create the client outside of the component, or store it with useState.

ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.

ℹ Unsafe fix: Store the client with React.useState.

4 │ ····const·[queryClient]·=·React.useState(()·=>·new·QueryClient());
│ + + +++++++++++++++++++++ +
import { QueryClient } from "@tanstack/react-query";
const useQueryClient = () => {
const queryClient = new QueryClient();
return queryClient;
};
code-block.js:4:25 lint/nursery/useTanstackQueryStableQueryClient  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━

✖ This QueryClient is created again each time the component renders.

3 │ const useQueryClient = () => {
> 4 │ const queryClient = new QueryClient();
│ ^^^^^^^^^^^^^^^^^
5 │ return queryClient;
6 │ };

ℹ Each new client starts with an empty cache, so the data that was already fetched is lost on every render.

ℹ Create the client outside of the component, or store it with useState.

ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.

ℹ Unsafe fix: Store the client with React.useState.

4 │ ····const·[queryClient]·=·React.useState(()·=>·new·QueryClient());
│ + + +++++++++++++++++++++ +
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useState } from "react";
const sharedClient = new QueryClient();
function App() {
const [queryClient] = useState(() => new QueryClient());
return (
<QueryClientProvider client={queryClient}>
<Home />
</QueryClientProvider>
);
}
async function ServerComponent() {
const queryClient = new QueryClient();
await queryClient.prefetchQuery(options);
}