PATTERN Cited by 1 source
Server-hydrate visible only¶
Intent¶
On a server-side-rendered page, hydrate only the portion of the page currently visible in the viewport. Mirror the client-side window virtualization choice at the SSR layer so the server-to-client handoff doesn't regenerate the full-page cost that virtualization was put there to avoid.
Context¶
Server-side rendering typically sends the full HTML + hydrates the whole page at boot. For a page with 10,000+ repeated items (diff lines, table rows, feed posts), this produces:
- Bloated initial HTML — tens of MB of markup.
- Slow time-to-interactive — hydration runs handler attachment + component-tree construction across every item, on the main thread.
- Large JS heap — the hydrated component tree is proportional to page content, not viewport size.
If the page already uses window virtualization client-side, the SSR pass undoes the win at hydration time. The fix is to align SSR with the virtualization choice.
Mechanism¶
- Determine the visible window on the server — typically defaults to "first N items" since the server doesn't know the client's scroll position. Pick N to cover above-the-fold plus a small overscan.
- Render only those items into the initial HTML with hydration markers.
- Render placeholder / skeleton markup for the rest (or emit nothing and rely on the virtualization library to create placeholders from scroll height).
- Hydrate only the rendered portion; rest becomes the client's responsibility as the user scrolls.
- Combine with progressive data loading to stream / background-fetch the rest so interaction can proceed in parallel with load completion.
Canonical instance¶
GitHub's Files-changed tab — "On the server side, we optimized rendering to hydrate only visible diff lines. This slashed our time-to-interactive and keeps memory usage in check, ensuring that even huge pull requests feel fast and responsive on load. Finally, with progressive diff loading and smart background fetches, users are now able to see and interact with content sooner. No more waiting for a massive number of diffs to finish loading." (Source: sources/2026-04-03-github-the-uphill-climb-of-making-diff-lines-performant)
Note: the GitHub source does not disclose the concrete SSR
substrate — whether it's React 18 streaming SSR
(concepts/streaming-ssr), classic renderToString + selective
hydration, or a custom diff-chunk delivery path. The behavioral
description is solid; the implementation shape isn't published.
Trade-offs¶
- Needs server-side knowledge of item size to pick N. Dynamic item sizes (diff lines with wrapping, inline comments) make this harder.
- Initial scroll-into-view not-yet-rendered can flash content as it hydrates. Skeletons mitigate but don't eliminate the perception.
- SEO / accessibility: crawlers and screen readers see only the initial window. For search-indexed content this can be disqualifying. GitHub PRs are authenticated, so SEO isn't a concern; public-content apps must verify.
- Does not help the initial-paint metric as much as streaming SSR does (concepts/streaming-ssr) — this is about hydration cost, not FCP.
Anti-patterns¶
- Full-page SSR + client virtualization. The worst of both worlds — server pays the full-render cost, hydration runs across all items, then virtualization throws most away.
- Hydrate-visible-only without virtualization. Leaves non-hydrated static markup in the DOM; interactions outside the hydrated window silently don't work.
- Skeleton states that look like real content. Confuses users who click before items hydrate.
Related¶
- concepts/window-virtualization — client-side counterpart.
- concepts/react-hydration — the hydration primitive being scoped.
- concepts/streaming-ssr — complementary SSR technique at the paint axis.
- concepts/dom-node-count — one of the costs this pattern controls.
- patterns/progressive-data-loading — typical partner pattern (stream / background-fetch so interaction can start before load completes).
- systems/github-pull-requests — canonical wiki instance.
- systems/react — framework context.