Browser, HTML/CSS & React — from markup to hydration
Core details
1. Browser: from bytes to pixels
The browser is a pipeline. Rough order (same vocabulary as Rendering pipeline & compositing):
- Parse HTML → DOM (live tree of nodes: elements, text, comments).
- Parse CSS → CSSOM (tree of style rules + @-rules).
- Style — match selectors to nodes, resolve cascade, produce computed values per element.
- Layout (“reflow”) — compute geometry (box model, flow, positioning).
- Paint — rasterize pictures (text, colors, images, effects) for regions that changed.
- Composite — merge layers into the frame shown on screen (GPU-backed where applicable).
HTTP response (HTML bytes)
│
▼
┌─────────┐ stylesheets, inline <style>
│ DOM │◄────────────────────────────┐
└────┬────┘ │
│ ┌─────▼─────┐
│ │ CSSOM │
└──────────┬──────────────┴─────┬─────┘
│ │
▼ ▼
computed styles ◄─── cascade (specificity,
order, inherit)
│
▼
layout → paint → composite → frameHTML’s job: encode structure and semantics (<main>, headings, landmarks). Accessibility and SEO lean on honest structure—not only div soup.
CSS’s job: encode presentation (which properties apply to which elements). The cascade decides which declaration wins when several rules target the same property: origin (user agent, author, user) → important → specificity → source order.
Modern platform levers: Web Components (Custom Elements, Shadow DOM, templates) can encapsulate browser-native UI; resource hints such as fetchpriority, loading="lazy", rel="preload", and rel="prefetch" control discovery order and must be tied to measured critical-path needs.
2. HTML loading & script execution (interview soundbite)
| Script mode | Typical effect |
|---|---|
Classic <script src> | Blocks HTML parsing until fetched & run (unless async/defer). |
defer | Fetch in parallel; run after HTML parse, in order. |
async | Fetch in parallel; run when ready—order not guaranteed vs other async scripts. |
Why it matters for React: your bundle must load and execute before hydration can attach event listeners and state to existing HTML.
3. Where React fits (mental model)
Without React (imperative): your code calls document.createElement, appendChild, className = … as data changes—easy to let DOM and in-memory state drift.
With React (declarative): you write functions that return a description of UI (React elements—plain objects: type, props, children). On each render, React compares the new tree to the previous one (reconciliation on the Fiber tree) and commits the minimal DOM mutations needed.
props + state
│
▼
render() ──► React element tree (descriptors)
│
▼
reconciliation (Fiber) ──► commit ──► DOM updates + effectsReact is not “the DOM”—it’s a scheduler + reconciler that owns updates to a DOM subtree (via react-dom). Concurrent React can interrupt low-priority rendering so input stays responsive (details).
4. Hydration — what actually happens
Problem: SSR (or SSG) sends HTML so the user sees content before JS is ready. That HTML is a snapshot: no React event handlers yet.
Hydration: when the client bundle runs, hydrateRoot/HydrationRoot (React 18+) walks the existing DOM, expects it to match what React would render for the same props/state snapshot, attaches internal Fiber state, registers event listeners, and from then on updates are incremental like a normal client app.
| Step | What happens |
|---|---|
| 1 | Server emitted HTML string (+ sometimes serialized data). |
| 2 | Browser paints visible content (good LCP / perceived perf). |
| 3 | JS loads → React hydrates root—reuse nodes where possible. |
| 4 | After hydration, state updates go through client React as usual. |
If server HTML ≠ first client render → hydration mismatch (warnings, broken nodes, duplicate UI). Common causes: Date.now(), Math.random(), window during render, suppressHydrationWarning misused, locale/timezone drift, or invalid HTML nesting that the parser “fixes” differently than React expects.
Next.js App Router note: React Server Components change the split: much UI never ships as client component JS—only client boundaries hydrate. See Next.js App Router for RSC vs “classic” SSR + client tree.
5. React core concepts (SDE3 checklist)
| Concept | One sentence |
|---|---|
| Component | Function (or class) that returns elements; capitalize in JSX. |
| Props | Inputs—treat as read-only; prefer key to reset state when identity changes. |
| State | Mutable over time via useState / useReducer; triggers re-render when updated. |
| Derived data | Compute in render (or useMemo when expensive)—don’t copy props to state without reason. |
Effects (useEffect) | Sync with systems outside React (fetch, subscriptions, timers)—not a replacement for event handlers. |
| Refs | Stable boxes for DOM nodes or values that must not trigger re-render on change. |
| Context | Dependency injection for trees—avoid one mega-context for app-wide churn. |
| Keys | Stable identity for list children—wrong keys → state leaks across rows. |
| Error boundaries | Catch render errors in children; event errors need try/catch inside handlers. |
| Strict Mode (dev) | Double-invokes some paths to surface non-idempotent effects. |
React 19-era apps also need fluency with Actions-oriented hooks (useActionState, useOptimistic, useFormStatus), Server Components, and compiler-assisted memoization where the project toolchain enables it.
Deeper Fiber, transitions, Suspense: React — rendering & architecture.
Understanding
The browser does not know React—it only knows DOM + CSS + JS. React’s value is discipline: a single equation UI = f(state, props) with a reconciliation engine that batches and prioritizes work. Hydration is the bridge between fast static HTML and interactive client trees; get the contract wrong and you pay in bugs, not just perf.
Senior understanding
| Probe | Strong angle |
|---|---|
| “Why SSR at all?” | TTFB/FCP, SEO, edge caching—not magic if hydration cost dominates |
| “CSR-only?” | Simpler mental model; worse first meaningful paint on slow devices unless shell is tiny |
| “CSS-in-JS vs modules?” | Invalidation cost on hot paths; SSR style extraction; team consistency |
Operational: log hydration warnings in staging; treat serialized state like an API with versioning.
Practical debugging path
When markup, styling, React, and hydration are all suspects, debug in this order:
- Confirm the browser layer first: valid HTML nesting, loaded CSS, script mode, and whether the expected root exists.
- Compare the server HTML snapshot with the first client render output before effects run.
- Identify whether the issue is DOM structure, CSS cascade, React reconciliation, or hydration contract.
- For React-specific bugs, inspect keys, state ownership, effects, and client/server component boundaries.
- For hydration bugs, remove nondeterministic inputs or serialize them explicitly rather than patching symptoms after mount.
Interview drill
Question: "Explain where React sits between HTML, CSS, and the browser."
Model answer structure:
- The browser owns DOM, CSSOM, style, layout, paint, composite, events, and resource loading.
- React produces element descriptions from props/state, reconciles them through Fiber, and commits changes through
react-dom. - CSS still resolves through the browser cascade; React does not replace layout or paint.
- SSR sends an HTML snapshot; hydration attaches React state and events to matching DOM.
- Strong production answers mention deterministic render, bundle cost, accessibility semantics, and telemetry for hydration warnings.
Follow-ups to expect:
- "Why can invalid HTML cause hydration mismatch?"
- "What changes with React Server Components?"
- "Why is SSR not automatically faster?"
Diagram — layers you own in production
┌──────────────────────────────────────────────┐
│ Your product (Next.js / Remix / SPA) │
│ ├── Routing, data loading, meta-framework │
│ └── Client boundaries / server components │
├──────────────────────────────────────────────┤
│ React (scheduler, reconciler, hooks model) │
├──────────────────────────────────────────────┤
│ react-dom (commit to DOM, hydration) │
├──────────────────────────────────────────────┤
│ Browser: DOM + CSSOM + layout + paint … │
└──────────────────────────────────────────────┘See also
Mark this page when you finish learning it.
Spotted something unclear or wrong on this page?