Foundations & Mental Model
Navigation lifecycle, critical rendering path, main thread, event loop, and perception budgets — the substrate everything else builds on.
You already know that “performance matters.” This module gives you the browser mental model experts use to reason about any metric — without re-explaining what HTTP is.
Navigation lifecycle
Every page load is a pipeline:
- Navigation start — user clicks, types URL, or SPA router fires
pushState - Connection — DNS, TCP, TLS (or QUIC for HTTP/3)
- Request/response — TTFB: time until first byte of HTML
- Parsing — HTML → DOM; CSS → CSSOM; JS may block parsing
- Render — render tree → layout → paint → composite
- Interaction — event listeners, hydration, long tasks compete for the main thread
Critical rendering path (CRP)
The CRP is the minimum work to show pixels:
| Stage | What happens | Perf lever |
|---|---|---|
| DOM | HTML parsed to nodes | Minimize blocking scripts in <head> |
| CSSOM | CSS parsed, selectors matched | Split non-critical CSS; avoid @import chains |
| Render tree | DOM + computed styles (visible nodes) | display:none subtrees excluded |
| Layout | Box geometry calculated | Avoid forced sync layout in hot paths |
| Paint | Pixels drawn into layers | Prefer compositor-only properties |
| Composite | Layers GPU-composited | transform / opacity are cheap |
DO inline critical above-the-fold CSS and defer the rest.
DO use defer or type="module" for non-critical scripts.
DON’T put large synchronous scripts in <head> — they halt DOM construction.
<style>
/* Critical: hero + header only */
.hero { min-height: 60vh; }
</style>
<link rel="preload" href="/styles/main.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<script type="module" src="/app.js"></script>
Main thread & event loop
JavaScript runs on the main thread alongside rendering. The event loop processes:
- Macrotasks —
setTimeout, I/O callbacks, user events - Microtasks —
Promise.then,queueMicrotask(drain completely before next macrotask) - Render steps — style, layout, paint (if needed)
A long task is any task > 50 ms. Long tasks block input handling and push INP/TBT up. The fix is not “faster JS” alone — it’s yielding so the browser can respond.
Event loop visualizer
RAIL & perception budgets
Google’s RAIL model (Response, Animation, Idle, Load) translates to budgets:
| Concern | Target |
|---|---|
| Input response | < 100 ms perceived |
| Animation frame | < 16 ms (60 fps) |
| Idle work chunks | < 50 ms |
| Meaningful paint | < 1 s perceived (context-dependent) |
Users forgive slower loads more than janky interactions — optimize responsiveness on interactive pages first.
Resource hint mental model
| Hint | Use when |
|---|---|
preconnect | You know the origin (fonts, API) — saves RTT on TLS |
dns-prefetch | Cheaper fallback for third parties |
preload | Same-origin asset needed now for this navigation |
prefetch | Asset for likely next navigation |
modulepreload | ES module graph for current page |
Mental model: Preconnect for domains, preload for viewport-critical assets, prefetch for futures.
What to learn next
Module 01 maps these pipeline stages to measurable metrics (FCP, LCP, INP, CLS). Module 02–05 show how to move each needle. Module 06 teaches measurement; Module 07 covers speculation rules, bfcache, and streaming.