---
title: TanStack Query
description: This doc describes TanStack Query integration.
nav: 4.03
---
[TanStack Query](https://tanstack.com/query/) provides a set of functions for managing async state (typically external data).
From the [Overview docs](https://tanstack.com/query/v4/docs/overview):
> React Query is often described as the missing data-fetching library for React, but in more technical terms, it makes **fetching, caching, synchronizing and updating server state** in your React applications a breeze.
[jotai-tanstack-query](https://github.com/jotai-labs/jotai-tanstack-query) is a Jotai integration library for TanStack Query. It provides a wonderful interface with all of the TanStack Query features, providing you the ability to use those features in combination with your existing Jotai state.
## Install
In addition to `jotai`, you have to install `jotai-tanstack-query` and `@tanstack/query-core` to use the integration.
```bash
yarn add jotai-tanstack-query @tanstack/query-core
```
## Exported functions
- `atomsWithQuery` for [QueryObserver](https://tanstack.com/query/v4/docs/reference/QueryObserver)
- `atomsWithInfiniteQuery` for [InfiniteQueryObserver](https://tanstack.com/query/v4/docs/reference/InfiniteQueryObserver)
- `atomsWithMutation` for MutationObserver
All three functions follow the same signature.
```ts
const [dataAtom, statusAtom] = atomsWithSomething(getOptions, getQueryClient)
```
The first `getOptions` parameter is a function that returns an input to the observer.
The second optional `getQueryClient` parameter is a function that return [QueryClient](https://tanstack.com/query/v4/docs/reference/QueryClient).
The return values have two atoms.
The first one is called `dataAtom` and it's an atom for the data from the observer. `dataAtom` requires Suspense.
The second one is called `statusAtom` and it's an atom for the full result from the observer. `statusAtom` doesn't require Suspense.
The data from the observer is also included in `statusAtom`,
so if you don't use Suspense, you don't need to use `dataAtom`.
## `atomsWithQuery` usage
`atomsWithQuery` creates new atoms that implement a standard [`Query`](https://tanstack.com/query/v4/docs/guides/queries) from TanStack Query.
> A query is a declarative dependency on an asynchronous source of data that is tied to a unique key. A query can be used with any Promise based method (including GET and POST methods) to fetch data from a server.
```jsx
import { atom, useAtom } from 'jotai'
import { atomsWithQuery } from 'jotai-tanstack-query'
const idAtom = atom(1)
const [userAtom] = atomsWithQuery((get) => ({
queryKey: ['users', get(idAtom)],
queryFn: async ({ queryKey: [, id] }) => {
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
return res.json()
},
}))
const UserData = () => {
const [data] = useAtom(userAtom)
return
{JSON.stringify(data)}
}
```
## `atomsWithInfiniteQuery` usage
`atomsWithInfiniteQuery` is very similar to `atomsWithQuery`, however it is for an `InfiniteQuery`, which is used for data that is meant to be paginated. You can [read more about Infinite Queries here](https://tanstack.com/query/v4/docs/guides/infinite-queries).
> Rendering lists that can additively "load more" data onto an existing set of data or "infinite scroll" is also a very common UI pattern. React Query supports a useful version of useQuery called useInfiniteQuery for querying these types of lists.
A notable difference between a standard query atom is the additional option `getNextPageParam` and `getPreviousPageParam`, which is what you'll use to instruct the query on how to fetch any additional pages.
```jsx
import { atom, useAtom } from 'jotai'
import { atomsWithInfiniteQuery } from 'jotai-tanstack-query'
const idAtom = atom(1)
const [userAtom] = atomsWithInfiniteQuery((get) => ({
queryKey: ['users', get(idAtom)],
queryFn: async ({ queryKey: [, id] }) => {
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
return res.json()
},
// infinite queries can support paginated fetching
getNextPageParam: (lastPage, pages) => lastPage.nextCursor,
}))
const UserData = () => {
const [data] = useAtom(userAtom)
return data.pages.map((userData, index) => (
{JSON.stringify(userData)}
))
}
```
### Fetching pages and refetching
Using the same atom as in the above example, we can dispatch an action to `userAtom`.
```js
const UserData = () => {
const [data, dispatch] = useAtom(userAtom)
const handleFetchNextPage = () => dispatch({ type: 'fetchNextPage' })
const handleFetchPreviousPage = () => dispatch({ type: 'fetchPreviousPage' })
}
```
## `atomsWithMutation` usage
`atomsWithMutation` creates new atoms that implement a standard [`Mutation`](https://tanstack.com/query/v4/docs/guides/mutations) from TanStack Query.
> Unlike queries, mutations are typically used to create/update/delete data or perform server side-effects.
```jsx
import { atom, useAtom } from 'jotai'
import { atomsWithMutation } from 'jotai-tanstack-query'
const idAtom = atom(1)
const [, postAtom] = atomsWithMutation((get) => ({
mutationKey: ['posts'],
mutationFn: async ({ title, body }) => {
const res = await fetch(`https://jsonplaceholder.typicode.com/posts`, {
method: 'POST',
body: JSON.stringify({ title, body, userId: get(idAtom) }),
headers: { 'Content-type': 'application/json; charset=UTF-8' },
})
const data = await res.json()
return data
},
}))
const PostData = () => {
const [post, mutate] = useAtom(postAtom)
return (
{JSON.stringify(post)}
)
}
```
## Referencing the same instance of Query Client in your project
Perhaps you have some custom hooks in your project that utilises the `useQueryClient()` hook to obtain the `QueryClient` object and call its methods.
To ensure that you reference the same `QueryClient` object, be sure to wrap the root of your project in a `` and initialise `queryClientAtom` with the same `queryClient` value you provided to `QueryClientProvider`.
Without this step, `useQueryAtom` will reference a separate `QueryClient` from any hooks that utilise the `useQueryClient()` hook to get the queryClient.
Alternatively, you can specify your `queryClient` with `getQueryClient` parameter.
### Example
In the example below, we have a mutation hook, `useTodoMutation` and a query `todosAtom`.
We included an initialisation step in our root `` node.
Although they reference methods same query key (`'todos'`), the `onSuccess` invalidation in `useTodoMutation` will not trigger **if the `Provider` initialisation step was not done.**
This will result in `todosAtom` showing stale data as it was not prompted to refetch.
```jsx
import { createStore } from 'jotai/vanilla'
import { Provider } from 'jotai/react'
import {
useMutation,
useQueryClient,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
import { atomsWithQuery, queryClientAtom } from 'jotai-tanstack-query'
const queryClient = new QueryClient()
export const App = () => {
// This Provider initialisation step is needed so that we reference the same
// queryClient in both atomWithQuery and other parts of the app. Without this,
// our useQueryClient() hook will return a different QueryClient object
const storeRef = useRef()
if (!storeRef.current) {
storeRef.current = createStore()
storeRef.current.set(queryclientAtom, queryClient)
}
return (
)
}
export const [todosAtom] = atomsWithQuery((get) => {
return {
queryKey: ['todos'],
queryFn: () => fetch('/todos'),
}
})
export const useTodoMutation = () => {
const queryClient = useQueryClient()
return useMutation(
async (body: todo) => {
await fetch('/todo', { Method: 'POST', Body: body })
},
{
onSuccess: () => {
void queryClient.invalidateQueries(['todos'])
},
onError,
}
)
}
```
## SSR support
All atoms can be used within the context of a server side rendered app, such as a next.js app or Gatsby app. You can [use both options](https://tanstack.com/query/v4/docs/guides/ssr) that React Query supports for use within SSR apps, [hydration](https://tanstack.com/query/v4/docs/guides/ssr#using-hydration) or [`initialData`](https://tanstack.com/query/v4/docs/guides/ssr#using-initialdata).
## Error handling
With `dataAtom`,
Fetch error will be thrown and can be caught with ErrorBoundary.
Refetching may recover from a temporary error.
See [a working example](https://codesandbox.io/s/joer59) to learn more.
## Devtools
In order to use the Devtools, you need to install it additionally.
```bash
$ npm i @tanstack/react-query-devtools
# or
$ pnpm add @tanstack/react-query-devtools
# or
$ yarn add @tanstack/react-query-devtools
```
All you have to do is put the `` in the ``.
```tsx
import {
QueryClientProvider,
QueryClient,
QueryCache,
} from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { queryClientAtom } from 'jotai-tanstack-query'
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: Infinity,
},
},
})
export const App = () => {
const storeRef = useRef()
if (!storeRef.current) {
storeRef.current = createStore()
storeRef.current.set(queryclientAtom, queryClient)
}
return (
)
}
```
## Examples
### Basic demo
### Devtools demo
### Hackernews