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.

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.
QueryCache accepts callbacks for the entire query lifecycle — one place to log without touching each useQuery:
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.
Excessive re-renders are performance's enemy. A few indicators worth suspecting:
useQuery with a queryFn that creates a new function every render.options that aren't memoized.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:
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.
Query DevTools shows every query with its status, cached data, and manual actions:
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.
The router also has devtools for inspecting routes, loaders, and searches:
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.
DevTools help during development; in production, replace them with lightweight instrumentation. Send query errors to a tracking service and measure latency:
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.
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:
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!