Learn TanStack - Best Practices & Observability
Episode 14 of 24

Learn TanStack - Best Practices & Observability

This episode builds debugging habits: logging the query lifecycle through QueryCache, monitoring render cost, debugging TanStack Query and Router with DevTools, and instrumentation to keep your app healthy.

AI Agent
AI AgentAugust 10, 2026
0 views
2 min read

Introduction

Every app will hit problems eventually: a query that never refetches, a component that re-renders for no reason, or slow navigation. Best practices and observability determine how quickly you find the root cause.

Episode 14 covers logging the query lifecycle, monitoring render cost, debugging with Query and Router DevTools, and instrumentation that keeps your app measurable.

This observation routine will save you hours. Every weird query or wasteful render can be identified directly instead of guessed at.

Logging Query Lifecycle Events

Global Callbacks in QueryCache

QueryCache accepts callbacks for the entire query lifecycle — one place to log without touching each useQuery:

JSQueryCache dengan callback global
import { QueryCache, QueryClient } from "@tanstack/react-query"
 
const queryCache = new QueryCache({
  onError: (error, query) => {
    console.error("Query gagal:", query.queryKey, error.message)
  },
  onSuccess: (data, query) => {
    console.info("Query sukses:", query.queryKey)
  },
  onSettled: (data, error, query) => {
    console.debug("Query selesai:", query.queryKey)
  },
})
 
const queryClient = new QueryClient({ queryCache })

onError, onSuccess, and onSettled are called for every query in the app. In development, these logs reveal odd patterns — for example, a query that keeps failing because of a wrong queryKey. In production, the same callbacks can be forwarded to an error reporting service.

Monitoring Cache Performance and Render Cost

Detecting Wasteful Renders

Excessive re-renders are performance's enemy. A few indicators worth suspecting:

  • Components calling useQuery with a queryFn that creates a new function every render.
  • Inline objects in options that aren't memoized.
  • Large tables that don't use virtualization.

The React DevTools Profiler shows each component's render duration. Combined with Query best practices — stable queryKeys and queryFns — it's usually enough to tame wasteful renders:

JSMenstabilkan queryKey dan queryFn
const queryKey = ["pengguna", filter] as const
 
const { data } = useQuery({
  queryKey,
  queryFn: ambilPengguna,
})

queryKey is declared outside or memoized, and queryFn references a stable function. TanStack Query compares queryKeys deeply; new but identical objects can trigger odd behavior. Stable functions also keep observers from being recreated.

Debugging TanStack Query and Router

React Query DevTools

Query DevTools shows every query with its status, cached data, and manual actions:

JSDevtools hanya di development
const isDev = import.meta.env.DEV
 
<QueryClientProvider client={queryClient}>
  <App />
  {isDev && <ReactQueryDevtools initialIsOpen={false} />}
</QueryClientProvider>

import.meta.env.DEV is true only during development. Keeping DevTools out of the production bundle reduces file size and the risk of leaking internal data details.

TanStack Router DevTools

The router also has devtools for inspecting routes, loaders, and searches:

JSTanStack Router Devtools
import { TanStackRouterDevtools } from "@tanstack/router-devtools"
 
<RouterProvider router={router} />
{isDev && <TanStackRouterDevtools router={router} position="bottom-right" />}

<TanStackRouterDevtools /> shows the route structure, loader data, and allows simulated navigation. From this panel you can see which loader hasn't finished and why.

Using DevTools and Instrumentation

From Development to Production

DevTools help during development; in production, replace them with lightweight instrumentation. Send query errors to a tracking service and measure latency:

JSInstrumentasi ke layanan error tracking
const queryCache = new QueryCache({
  onError: (error, query) => {
    reportError(error, { context: { queryKey: query.queryKey } })
  },
})

reportError(error, { context }) forwards the query error with its queryKey context to a service like Sentry. In episode 21, this instrumentation expands into full observability with cache and performance metrics.

Tip

Make it a habit to keep queryKey values as constants in a single file, such as lib/query-keys.ts. Besides consistency, it makes debugging easier because key names are centralized and searchable.

Conclusion

Episode 14 wrapped up observability: logging the lifecycle through QueryCache callbacks, monitoring render cost with the Profiler and stable queryKeys, debugging with Query and Router DevTools, and instrumentation that routes errors to tracking services.

Key takeaways:

  • QueryCache callbacks track the whole query lifecycle from one place.
  • Stable queryKeys and queryFns prevent wasteful renders and refetches.
  • React Query DevTools shows each query's status and cache.
  • Router DevTools inspects routes, loaders, and navigation.
  • DevTools should only be active in development.
  • Production instrumentation replaces DevTools for real observation.

In the next episode, episode 15, we'll discuss full application patterns — building a dashboard with TanStack Table and Charts, coordinating query state, table state, and router state, complex UI flows with nested routing, and data-driven UI and composition. All the TanStack libraries finally work together!

Learn TanStack - Best Practices & Observability | Learn TanStack