awesome-everything RU
↑ Back to the climb

Browser & Frontend Runtime

Render strategies: code and config reading

Crux Read real React/Next snippets — a hydration-mismatch source, an SSR data-fetch race, an ISR revalidate config, and a Suspense placement — and pick the highest-leverage fix.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at senior altitude — in orbit
◷ 14 min

Render-strategy bugs are read in code and config, not in the docs. Read each snippet, predict what it does at runtime, and choose the fix a senior engineer would make first.

Goal

Practise the loop every render-strategy incident runs: read the component or config, predict the hydration, freshness, or streaming behaviour, and reach for the structural fix — not a patch over the symptom.

Snippet 1 — the greeting that flickers

function Greeting() {
  // read during render, on BOTH server and client
  const hour = new Date().getHours();
  const msg = hour < 12 ? 'Good morning' : 'Good evening';
  return <h1>{msg}</h1>;
}
Quiz

This component throws 'Text content did not match' on hydration and spikes CLS. Where is the bug and what is the correct fix?

Snippet 2 — the SSR data fetch

// Client Component, server-rendered then hydrated
function Profile({ userId }) {
  const [user, setUser] = useState(null);
  useEffect(() => {
    fetch(`/api/users/${userId}`).then(r => r.json()).then(setUser);
  }, [userId]);
  return <span>{user?.name ?? 'Loading…'}</span>;
}
Quiz

The page is server-rendered, but this component always paints 'Loading…' in the initial HTML and the real name appears only after hydration. Why, and what is the higher-leverage approach?

Snippet 3 — the ISR config

// Next.js App Router route segment
export const revalidate = 3600; // seconds

async function ProductPage({ params }) {
  const product = await getProduct(params.id); // cached, revalidated hourly
  return <ProductView product={product} />;
}
Quiz

Prices change unpredictably, sometimes minutes apart, but `revalidate = 3600` means a price can be stale for up to an hour. How do you keep ISR's static-delivery win while bounding staleness to seconds?

Snippet 4 — the Suspense placement

async function Dashboard() {
  return (
    <Shell>
      <Stats />                       {/* fast: ~10 ms */}
      <Suspense fallback={<Spinner/>}>
        <OrderHistory />              {/* slow: ~400 ms query */}
      </Suspense>
    </Shell>
  );
}
Quiz

With streaming SSR (renderToPipeableStream), what does this Suspense placement achieve, and what would happen if the boundary were removed?

Recap

Four reading patterns cover most render-strategy incidents: a live value (new Date(), Math.random(), window) read during render is a hydration mismatch — defer it to useEffect; useEffect never runs during SSR, so data must be fetched on the server and passed down (or embedded in a dehydrated cache) to appear in the initial HTML; time-based ISR bounds staleness loosely, but on-demand revalidation via webhook tightens it to seconds while keeping CDN delivery; and a Suspense boundary around a slow query is what lets streaming SSR flush the shell first instead of blocking on the slowest part. Diagnose from the code and config, fix the structure, then verify TTFB, INP, and the mismatch count.

Continue the climb ↑Render strategies: a per-route rendering audit
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.