Quick start
1. Set up the QueryClient
The QueryClient is the same object used by TanStack Query. Create one and call .mount() so it can subscribe to focus / online events. Then register it with setQueryClient — factories will use it automatically.
import { QueryClient } from '@tanstack/query-core'import { setQueryClient } from '@effector-tanstack-query/core'
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } },})queryClient.mount()setQueryClient(queryClient)For SSR / per-request isolation, inject the client per scope via fork({ values: [[$queryClient, queryClient]] }) instead — see SSR.
2. Create a query
import { createQuery } from '@effector-tanstack-query/core'
export const userQuery = createQuery({ name: 'user', queryKey: ['user', 1], queryFn: () => fetch('/api/users/1').then((r) => r.json()),})Passing the client explicitly still works:
createQuery(queryClient, options).
The name is optional but strongly recommended — it gives the internal stores stable SIDs so they round-trip via serialize(scope) / fork({ values }) for SSR. (Why?)
3. Read its state
createQuery returns an object with effector stores and events:
userQuery.$data // Store<User | undefined>userQuery.$error // Store<Error | null>userQuery.$status // Store<'pending' | 'success' | 'error'>userQuery.$isPending // Store<boolean>userQuery.$isFetching // Store<boolean>userQuery.mounted // EventCallable<void> — start the subscriptionuserQuery.refresh // EventCallable<void> — invalidate + refetchDrive the lifecycle yourself, then read state:
userQuery.mounted() // observer subscribes; query starts fetching// ...await somethingconsole.log(userQuery.$data.getState())In tests with fork, inject a fresh client per scope:
import { fork, allSettled } from 'effector'import { $queryClient } from '@effector-tanstack-query/core'
const queryClient = new QueryClient()queryClient.mount()
const scope = fork({ values: [[$queryClient, queryClient]] })await allSettled(userQuery.mounted, { scope })expect(scope.getState(userQuery.$data)).toEqual({ id: 1, name: 'Alice' })4. Use in React (optional)
import { useQuery } from '@effector-tanstack-query/react'
function UserProfile() { const { data, isPending, error, refresh } = useQuery(userQuery)
if (isPending) return <p>Loading…</p> if (error) return <p>Error: {error.message}</p>
return ( <div> <h1>{data.name}</h1> <button onClick={refresh}>Refresh</button> </div> )}The hook calls mounted() on mount and unmounted() on cleanup automatically.
5. Make the key reactive
Drop a Store into queryKey and the query refetches automatically when it updates.
import { createStore, createEvent } from 'effector'
const setUserId = createEvent<number>()const $userId = createStore(1).on(setUserId, (_, id) => id)
const userQuery = createQuery({ name: 'user', queryKey: ['user', $userId], queryFn: ({ queryKey }) => fetch(`/api/users/${queryKey[1]}`).then((r) => r.json()),})
userQuery.mounted()setUserId(2) // → fires a refetch with key ['user', 2]What’s next
- Read Queries for
enabled,placeholderData,select, andrefetchInterval. - Read Mutations for
mutateWith,finishedevents,createInvalidate, and offline behavior. - For full type signatures, see the API reference.
- Browse runnable apps in
examples/:examples/csr(Vite + React, every common pattern) andexamples/ssr(Next.js App Router withquery.prefetch).