Building a Real-Time Dashboard with TanStack Query and WebSockets
TanStack Query (formerly React Query) is one of those libraries that, once you use it, you wonder how you ever managed without it. The combination of automatic background refetching, stale-while-revalidate caching, and dead-simple deduplication handles 90% of data fetching use cases elegantly.
But then there's the 10%: live data that needs to update in real time without polling.
The challenge
I was building an analytics dashboard that showed live request counts, error rates, and latency percentiles. The data needed to update every few seconds, and polling felt wasteful — we'd be hitting the database on every interval regardless of whether anything had changed.
WebSockets were the obvious answer, but I didn't want to abandon TanStack Query's caching layer. I wanted the best of both worlds: live updates and the ability to hydrate from a server-side initial fetch.
The approach
TanStack Query exposes a queryClient that lets you manually set and invalidate cached data. Combined with a WebSocket connection, you can push live updates directly into the cache:
const queryClient = useQueryClient()
useEffect(() => {
const ws = new WebSocket('wss://api.example.com/metrics/live')
ws.onmessage = (event) => {
const metrics = JSON.parse(event.data)
queryClient.setQueryData(['metrics', 'live'], metrics)
}
return () => ws.close()
}, [queryClient])
The consuming component doesn't need to know anything about WebSockets — it just uses useQuery as normal:
const { data: metrics } = useQuery({
queryKey: ['metrics', 'live'],
queryFn: fetchMetrics, // initial fetch
staleTime: Infinity, // don't auto-refetch; WS handles updates
})
The result
The dashboard now hydrates instantly on load from the server-side fetch, then transitions seamlessly to live WebSocket updates. If the WebSocket disconnects, TanStack Query's retry logic kicks in for the initial fetch, giving us a graceful fallback.
The pattern is simple enough that I've extracted it into a small hook we reuse across several dashboard pages. The key insight is that TanStack Query's cache is just a map — anything that can write to it can serve as a data source, not just HTTP requests.