TanStack Query
Production Pattern
Telemetry
Rendered at
Waiting for browser render
Data fetched at
Pending
Render source
Browser
Hydrated
No
Request count
0
Last update
Pending
Cache status
No cache
Update mode
Blocking fetch
The server sends a light shell. The browser does the data fetch and paints the real content.
Initial HTML
Browser
Initial Paint State
Data in initial HTML
No
HTML generated by
Browser
User sees first
Loading state until hydration runs
Real-Time Rendering Timeline
Request
Document request started
HTML
HTML response received
FCP
First contentful paint
JS
Client JavaScript running
Hydration
React effects can run
Fetch
Client data request started
Fetch done
Client data response resolved
UI
React committed new data
Live Comparison
TanStack Query
Telemetry
Rendered at
Waiting for browser render
Data fetched at
Pending
Render source
Browser
Hydrated
No
Request count
0
Last update
Pending
Cache status
No cache
Update mode
Blocking fetch
useEffect Fetch
Telemetry
Rendered at
Waiting for browser render
Data fetched at
Pending
Render source
Browser
Hydrated
No
Request count
0
Last update
Pending
Cache status
No cache
Update mode
Blocking fetch
What to notice
Initial render
Initial render is browser-driven.
Production update
Cached + background refetch
Naive update
Initial loader + client fetch
Where HTML is generated
Browser
When
After JavaScript downloads and runs
Rendered at
Waiting for browser render
The browser asks Next.js for /csr.
Next.js returns minimal HTML plus the client JavaScript bundle.
React becomes interactive, then the client component starts fetching posts.
The network request happens after the page is already open.
React updates state, removes the loader, and paints the posts.
Now happening
Browser: The browser asks Next.js for /csr.
Behind the scenes
Browser
Request page
Next.js Server
Send app shell
Browser
Hydrate and run useEffect
Data API
Fetch posts from browser
Browser
Render final UI
Client component
The CSR demo uses a client component because useEffect and useState only run in the browser.
Loader is expected
The loader appears because data is not part of the first HTML response.
Live data stays separate
The live comparison updates after the app is interactive, independent of the initial render.