Skip to main content

Options reference

Every option a query, an infinite query or a mutation takes, with its type, its default and what it does. The guides explain when to reach for each one; this page is the table to look things up in. The concept page is describing a query once. A row names the TanStack Query spelling only where it differs from the Dart one.

How a field gets its value​

null means "not configured" on every field below. A field that QueryDefaults or MutationDefaults also has takes, when left null, in this order:

  1. the defaults registered for a matching key with client.setQueryDefaults / client.setMutationDefaults (several matching prefixes merge in the order their keys were first registered, the later one winning per field),
  2. the client-wide DefaultOptions passed to QueryClient(defaultOptions:) or setDefaultOptions,
  3. the built-in default in the Default column.

So queryFn, mutationFn, structuralSharing, scope and meta, which the defaults layer can also supply, show only the built-in fallback below. The other fields — initialData and its two timestamps, select, placeholderData, the paging fields, mutationKey, mutationFnWithContext and the mutation callbacks — have no defaults layer: unset is unset.

An option that can be switched off says so with a value — Enabled.no, RetryPolicy.never, RefetchInterval.off — never with null. The value types are listed at the end of this page. The defaults layer is on the client reference.

An example, from a shop app's lib/data/product_queries.dart:

QueryObserverOptions<Product> productQuery(ProductRepository repo, String id) =>
QueryObserverOptions(
queryKey: QueryKey(<Object?>['products', 'detail', id]),
queryFn: (context) => repo.product(id, signal: context.signal),
// A price may change, but not every second.
staleTime: const StaleTime.duration(Duration(minutes: 2)),
// Kept for a while after the detail screen closes, for the back button.
gcTime: const GcTime.duration(Duration(minutes: 10)),
retry: const RetryPolicy.times(2),
refetchOnWindowFocus: RefetchOn.never,
);

The option classes​

ClassTakesUsed by
QueryOptions<TQueryData>the cache fieldsclient.query — fetch once and complete with the data
QueryObserverOptions<TData>cache fields + observer fields, no selectevery widget call style, client.observe, QueryObserver
QuerySelectOptions<TQueryData, TData>the same, select requiredthe same, when the reader sees a projection
InfiniteQueryOptions<TPageData, TPageParam>cache fields + paging fieldsclient.infiniteQuery (and client.query)
InfiniteQueryObserverOptions<TPageData, TPageParam>cache + paging + observer fields, no selectthe infinite call styles, client.observeInfinite
InfiniteQuerySelectOptions<TPageData, TPageParam, TData>the same, select requiredthe same, with a projection of the pages
MutationOptions<TData, TVariables, TOnMutateResult>the mutation fieldsevery mutation call style, MutationObserver

The observer shapes extend the cache shape, so one options function serves client.query and a widget alike. QueryObserverOptions.withSelect(select) and InfiniteQueryObserverOptions.withSelect(select) turn a plain shape into its select shape with every other field carried over. Every class has copyWith, which leaves a field it is not handed as it was (handing one of initialDataUpdatedAt and initialDataUpdatedAtCompute clears the other) — except MutationOptions, which has none. The infinite shapes' copyWith throws ArgumentError for a queryFn, and the observer shapes' for pages.

None of these classes has value equality, on purpose: options built inline in build are handed to the observer on every build, and the observer compares the values they resolve to. An inline queryFn closure is a new function on every build, so the resolved options differ and the query cache reports a QueryObserverOptionsUpdated event; it does not refetch and does not restart the stale or polling timers, which follow the query, enabled and the resolved staleTime and refetchInterval.

Cache fields​

On QueryOptions and so on every query shape. They describe the cache entry, which every reader of the key shares.

FieldTypeDefaultWhat it does
queryKeyQueryKeyrequiredThe key the entry is cached under. A key holds one exact type; reading it as another throws QueryDataTypeError.
queryFnQueryFn<TQueryData>? — FutureOr<TQueryData> Function(QueryFunctionContext)noneFetches the data. With none here or in the defaults, a fetch fails with MissingQueryFunctionError, which is never retried. Not on the infinite shapes, which take pageFn.
enabledEnabled?Enabled.yesWhether the query fetches on its own. A disabled query still serves cached data and can be refetched by hand.
staleTimeStaleTime?StaleTime.zeroHow long fetched data counts as fresh.
gcTimeGcTime?five minutes (GcTime.defaultValue)How long the entry stays cached after its last reader leaves.
retryRetryPolicy?RetryPolicy.times(3); for client.query with no retry configured anywhere, no retriesWhether and how often a failed fetch is retried.
retryDelayRetryDelay?1 s doubling, at most 30 s (RetryDelay.defaultValue)The wait between attempts.
networkModeNetworkMode?NetworkMode.onlineHow connectivity gates the fetch. See network mode.
initialDataInitialData<TQueryData>?noneSeed data written into the cache as if fetched. See initial data.
initialDataUpdatedAtDateTime?none: the seed counts as fetched when writtenWhen the seed was fetched, for the staleness clock. TanStack: initialDataUpdatedAt as a number.
initialDataUpdatedAtComputeDateTime? Function()?noneThe same, computed only when data is actually seeded; null from it means now. Setting both forms throws ArgumentError when the options are resolved. TanStack: initialDataUpdatedAt as a function.
structuralSharingStructuralSharing<TQueryData>? — TQueryData Function(TQueryData? previous, TQueryData next)replaceEqualDeepHow new data is reconciled with what is cached. noStructuralSharing() turns it off. See structural sharing. TanStack spells the opt-out structuralSharing: false.
metaObject?noneFree-form data, handed to the query function as context.meta and readable off the query.

The query function receives a QueryFunctionContext: client, queryKey, meta and signal, a QueryCancelToken. Reading signal is what makes the fetch cancellable; see query cancellation.

Observer fields​

On QueryObserverOptions, QuerySelectOptions and the infinite observer shapes. They describe one reader, so two widgets watching one key may poll, refetch and select differently.

FieldTypeDefaultWhat it does
selectSelectFn<TQueryData, TData> — TData Function(TQueryData)required on the select shapes, absent on the plain onesProjects the cached data into what this reader sees. While the projection stays equal, data keeps its instance (unless structuralSharing is switched off); the rest of the result (fetchStatus, dataUpdatedAt, …) still changes and still notifies — buildWhen narrows rebuilds. A throwing select makes the result a QueryError. See render optimizations.
placeholderDataPlaceholderData<TQueryData>?noneData shown while the query is pending with no data of its own — not while it is in an error state, though a refetch after the error shows it again. Never cached; the result reports isPlaceholderData. See placeholder data.
refetchOnMountRefetchOn?RefetchOn.ifStaleWhether this reader subscribing triggers a refetch.
refetchOnWindowFocusRefetchOn?RefetchOn.ifStaleWhether the app returning to the foreground triggers a refetch. See app focus refetching.
refetchOnReconnectRefetchOn?RefetchOn.ifStale; RefetchOn.never under NetworkMode.alwaysWhether the network coming back triggers a refetch.
refetchIntervalRefetchInterval?RefetchInterval.offPolls while this reader is subscribed and the query is enabled, stale or not, a StaleTime.static query included. See polling.
refetchIntervalInBackgroundbool?falseWhether polling continues while the app is not focused.
retryOnMountbool?trueWhether a query in an error state with no data is fetched again when a reader subscribes (one holding data follows refetchOnMount). TanStack also takes a function of the query here.

Not here: notifyOnChangeProps (whole results are compared; buildWhen on every read narrows rebuilds), throwOnError (errors are a case of the sealed result), queryKeyHashFn (QueryKey is a value type), subscribed and experimental_prefetchInRender. See differences from TanStack Query.

Infinite query fields​

On InfiniteQueryOptions and the two infinite observer shapes, in addition to the cache fields (without queryFn) and, on the observer shapes, the observer fields. The data type is InfiniteData<TPageData, TPageParam>: pages and pageParams. See infinite queries.

FieldTypeDefaultWhat it does
pageFnInfinitePageFn<TPageData, TPageParam> — FutureOr<TPageData> Function(InfinitePageContext<TPageParam>)requiredFetches one page. Its context carries a typed pageParam, the direction, queryKey, client, meta and signal. TanStack: queryFn.
initialPageParamTPageParamrequiredThe param the first page is fetched with.
getNextPageParamPageParamFn<TPageData, TPageParam> — TPageParam? Function(page, pages, pageParam, pageParams)requiredThe param of the page after the last one; null means there is none, so hasNextPage is false.
getPreviousPageParamPageParamFn<TPageData, TPageParam>?none: hasPreviousPage is always falseThe same, backwards from the first page.
maxPagesint?none: every page is kept (0 too)How many pages to keep. A page fetch past the limit drops one page from the far end.
pagesint?none: one page into an empty query, every held page on a refetchHow many pages to fetch up front. Only on InfiniteQueryOptions, for client.infiniteQuery (or client.query); the observer shapes refuse it. A count handed to the client stays on the shared query and shapes later refetches that bring no options of their own (invalidateQueries, refetchQueries) until an observer on the key installs its own options — creating or subscribing one, or any setOptions, does that without a fetch.

A refetch of an infinite query — invalidation, focus, polling — requests the held pages again, first to last, each with the param computed from the page before it, and stops early if getNextPageParam returns null. hasNextPage, fetchNextPage and their backward twins live on the infinite observer and controller, not on the result; see results.

Mutation fields​

On MutationOptions. The three type arguments are what the function returns, what it is called with, and what onMutate returns. MutationOptions.simple(...) takes the same fields minus onMutate and fixes the third type to void, so the other two infer from mutationFn. See mutations.

FieldTypeDefaultWhat it does
mutationKeyQueryKey?noneAddresses the mutation for filters, isMutating, mutation state and setMutationDefaults.
mutationFnMutationFn<TData, TVariables>? — FutureOr<TData> Function(TVariables)nonePerforms the write. With none here or in the defaults, a run fails with MissingMutationFunctionError, not retried.
mutationFnWithContextMutationFnWithContext<TData, TVariables, TOnMutateResult>? — FutureOr<TData> Function(TVariables, MutationFunctionContext)noneThe same with a context: client, meta, mutationKey, onMutateResult and a signal that cancel() cancels. Set one of the two functions, never both (an assertion in debug builds, ArgumentError when resolved); when set it wins over a default mutationFn. TanStack: mutationFn's second argument.
onMutateOnMutate<TVariables, TOnMutateResult>? — FutureOr<TOnMutateResult?> Function(TVariables)noneRuns when the mutation is submitted, before the function; its result is handed to the other callbacks, typically a rollback snapshot. A throw fails the mutation without running the function.
onSuccessOnMutationSuccess? — (data, variables, onMutateResult)noneRuns after MutationCache.onSuccess. Awaited; a throw turns the success into an error.
onErrorOnMutationError? — (error, stackTrace, variables, onMutateResult)noneRuns after MutationCache.onError, retries spent. Awaited; a throw is reported to the zone and does not replace the error.
onSettledOnMutationSettled? — (data, error, stackTrace, variables, onMutateResult)noneRuns last, on success and error alike. The mutation stays pending until a returned future completes.
retryRetryPolicy?RetryPolicy.neverWhether a failed attempt is retried. TanStack's default is retry: 0.
retryDelayRetryDelay?1 s doubling, at most 30 sThe wait between attempts.
networkModeNetworkMode?NetworkMode.onlineOffline, an online mutation pauses and a mounted client resumes it on reconnect.
gcTimeGcTime?five minutesHow long a settled mutation stays in the cache once nothing observes it.
scopeMutationScope?none: unscoped mutations run in parallelMutations with equal scopes run one at a time, in submission order. Fixed for a run once it starts. See mutation scopes. TanStack: scope: { id }.
metaObject?noneFree-form data, readable as mutation.meta and context.meta.

The callbacks run in this order, each returned future awaited before the next: MutationCache.onMutate, onMutate; after the function, MutationCache.onSuccess (or onError), onSuccess (or onError), MutationCache.onSettled, onSettled. Callbacks have no default layer: setMutationDefaults carries none.

Per-call callbacks​

mutate(variables, callbacks: MutateCallbacks(...)) adds callbacks for one call — closing a dialog, showing a snack bar. MutateCallbacks has onSuccess, onError and onSettled with the signatures above, each optional. They run after the options' callbacks, only while the observer still has a listener, are not awaited, and a throw in one is reported to the zone. TanStack Query: the second argument of mutate.

Option values​

Each option with modes is a small sealed family of const values. A value built inline compares equal to an equal one, so a rebuild with the same value is not a change; the computed forms compare equal when their function is the same (a tear-off, not an inline closure). Each type also has the method the library resolves it with (resolve, shouldRetry, …); application code rarely calls them. Each case is a public class (StaleTimeDuration, EnabledWhen, RetryTimes, …), so a switch can name them.

TypeSpellings
StaleTimeStaleTime.zero (default), StaleTime.duration(d), StaleTime.infinite (never stale by time, invalidation still works), StaleTime.static (never stale; skipped by the mount, focus and reconnect refetches and by refetchQueries and invalidation while observed; refetchInterval still polls it), StaleTime.dynamic((query) => StaleTime). TanStack: 0, a number of ms, Infinity, 'static', a function.
GcTimeGcTime.duration(d), GcTime.defaultValue (five minutes), GcTime.never; GcTime.longest(a, b) picks the longer of two. TanStack: a number of ms, Infinity.
EnabledEnabled.yes (default), Enabled.no, Enabled.when((query) => bool). TanStack: true, false (also standing in for queryFn: skipToken), a function.
RetryPolicyRetryPolicy.never, RetryPolicy.always, RetryPolicy.times(n) (n retries, n + 1 attempts), RetryPolicy.when((failureCount, error, stackTrace) => bool) — failureCount is 0 on the first decision. TanStack: false, true, a number, a function.
RetryDelayRetryDelay.defaultValue, RetryDelay.exponential({base = 1 s, maximum = 30 s}) — with no arguments the default, RetryDelay.fixed(d), RetryDelay.dynamic((failureCount, error) => Duration). TanStack: the default function, a number of ms, a function.
RefetchOnRefetchOn.ifStale (default), RefetchOn.always, RefetchOn.never, RefetchOn.when((query) => RefetchOn). TanStack: true, 'always', false, a function.
RefetchIntervalRefetchInterval.off (default), RefetchInterval.every(d), RefetchInterval.dynamic((query) => Duration?) — null, zero or a negative duration stops polling. TanStack: false, a number of ms, a function.
NetworkMode (an enum)online (default), always, offlineFirst.
InitialDataInitialData.value(data), InitialData.compute(() => data?) — null from the callback means no seed. TanStack: a value, a function.
PlaceholderDataPlaceholderData.keepPrevious(), PlaceholderData.value(data), PlaceholderData.compute((previousData, previousQuery) => data?). TanStack: keepPreviousData, a value, a function.
MutationScopeMutationScope(id) — equal ids are one scope. TanStack: { id }.

.value(null) is a seed (or placeholder) of null for a nullable type; only a .compute returning null means "none". That is the one place where Dart's single null carries two meanings.