← Back to Blog

TanStack Query + TanStack DB: Predictable Data and Local Editing

2026-01-05

TanStack QueryTanStack DBReactTypeScriptStateCaching

TanStack Query + TanStack DB: Predictable Data and Local Editing


The biggest issues I see in data-heavy React apps are consistency issues: unstable keys, ad-hoc invalidation, and unclear ownership of state.


These are the patterns I use to keep it boring and predictable.


1) TanStack Query: treat query keys like an API


If caching feels like magic, your keys are probably unstable. Fix it with a key factory.


type Filters = { q?: string; status?: "active" | "archived" }

export const userKeys = {
  all: ["users"] as const,
  list: (filters: Filters) => [...userKeys.all, "list", filters] as const,
  detail: (id: string) => [...userKeys.all, "detail", id] as const,
}

Usage


useQuery({
  queryKey: userKeys.detail(id),
  queryFn: () => api.users.get(id),
  staleTime: 30_000,
})

Invalidation


await updateUser.mutateAsync(input)
queryClient.invalidateQueries({ queryKey: userKeys.all })

Rule: invalidate by “area”, not by guessing exact keys.


2) TanStack DB: local-first state without the chaos


TanStack DB works best when you treat it like a local model that can sync — not like “yet another global store”.


The pattern is simple:


  • collections hold data
  • hooks expose read/write
  • components stay dumb

  • 3) Dirty tracking is not optional


    If you edit locally, you need a real definition of “changed”. Make it explicit and test it.


    import isEqual from "lodash.isequal"
    
    const isDirty = !isEqual(remoteValue, localDraft)
    

    Rules I follow


  • never mutate collections directly inside components (wrap writes in hooks/services)
  • decide sync rules early (optimistic vs server-authoritative)
  • define conflict behaviour, even if it’s “rare”
  • add logging/telemetry around sync failures (they will happen)