Module IV · Viewing

The resource model

Module 3 ended with a DOM and a list: the tree the parser built, plus all the things it referenced and does not yet have. A real page is rarely one file. It is an HTML entry point that points at stylesheets, scripts, images, fonts, and embedded documents — each of which is a separate request, some of which point at further resources of their own. A page is a dependency tree, and loading it is a resolution problem. This module is how the browser discovers, prioritizes, and fetches everything a document depends on, and why the order it does so determines how fast the page feels.

4.1 A page is not a file

This is the single shift in thinking the module asks for. The index.html you author is the root of a tree, not the whole thing. Demo Company makes the tree visible by building it one branch at a time: the home page needs only itself and a favicon; the image page adds a .webp; the CSS page adds a stylesheet; the font page adds a font discovered inside that stylesheet; the JavaScript page adds a local script and a third-party one from a CDN; the iframe page adds an entire embedded document with its own dependencies. Each page prints its own request list, so you can read the dependency tree off the screen.

A page as a dependency treeThe index.html document is the root. It references a stylesheet, a script, an image, and an embedded iframe document. The stylesheet in turn references a web font and a background image, which are discovered only after the stylesheet loads and parses.index.htmlmain.cssapp.jscat.webpiframe docfont.woff2hero.webpdiscovered only after main.css loads and parses
Figure 4.1 — The document is the root; its references are branches; and some branches have branches. The amber resources live inside the CSS, so the browser cannot even know they exist until the stylesheet has arrived and been parsed. Depth in this tree is latency.

4.2 Discovery: finding the dependencies

The browser learns of most dependencies while parsing, as the tokenizer and tree builder of Module 3 encounter <link>, <script>, <img>, and <iframe> elements. But discovery is not uniform. A font referenced by @font-face or a background image set in url() is invisible until the CSS itself has been fetched and parsed — the browser has to load one resource to find out it needs another. That is the amber layer in Figure 4.1, and it is why a web font often starts downloading conspicuously late.

Worse, discovery competes with the parser-blocking problem from Module 3: a synchronous <script> stops the parser, which would stop discovery of everything below it in the document. The browser's answer is the preload scanner — a secondary, lightweight pass that races ahead through the raw bytes while the main parser is blocked, finds src and href references, and starts fetching them early. Discovery order and network order are therefore not the same thing, and a good deal of browser engineering exists to make the network busy as early as possible.

4.3 The waterfall

Plot every request against time and you get the waterfall — the most important diagnostic view in web performance, and the one in your DevTools Network panel. Each bar is one resource; its horizontal position is when it started and how long it took; its vertical stacking shows what happened in parallel and what had to wait.

A request waterfallThe HTML document loads first. The stylesheet and image and a deferred script load in parallel soon after. The web font cannot start until the stylesheet has finished, forming a dependency chain. First paint occurs after the render-blocking HTML and CSS are ready, before the font and image necessarily finish.index.htmlmain.csscat.webpapp.js (defer)font.woff2first painttime →the font waits on the CSS — a two-deep chain
Figure 4.2 — A waterfall. The HTML arrives, then the stylesheet, image, and deferred script fetch in parallel. The font cannot begin until the CSS that references it has parsed (the dashed dependency). First paint can happen once the render-blocking resources are ready, even before the font and image finish.

Two structural facts jump out of any waterfall. Parallelism is limited — over HTTP/1.1 a browser opens only a handful of connections per host, so requests queue; HTTP/2 and HTTP/3 multiplex many over one connection and largely dissolve that queue (Module 2). And chains are expensive — a resource that depends on another cannot start until the first finishes, so a three-deep chain (HTML → CSS → font) costs three sequential round trips no amount of bandwidth can collapse. The art of loading performance is turning chains into parallel fetches.

4.4 Render-blocking, async, and defer

Not all dependencies are equal in how they interrupt the page. Two behaviors dominate, and controlling them is most of what front-end performance work consists of.

CSS is render-blocking by default. The browser will not paint until it has the CSS, because painting first and restyling second would flash unstyled content at the user. So a stylesheet in the head holds back the first paint of the entire page — which is usually what you want, and is why the critical CSS should be small and fast.

Scripts choose their blocking behavior. A plain <script> is parser-blocking and ordered; the modern attributes change that:

Script loading strategies comparedA plain script stops HTML parsing while it downloads and executes, then parsing resumes. An async script downloads in parallel and executes as soon as it is ready, interrupting parsing at an unpredictable point and not guaranteeing order. A defer script downloads in parallel and executes in order only after parsing completes.<script>parsing STOPS while it downloads + runsasyncdownloads in parallel, runs as soon as ready (order not guaranteed)deferdownloads in parallel, runs in order after parsing finishesparsedownloadexecuteparser blocked
Figure 4.3 — The same script, three behaviors. A plain tag freezes the parser; async fetches alongside parsing and runs whenever it lands; defer fetches alongside parsing but runs in order after the document is built. For a script that touches the DOM, defer is almost always the right default. (Module-type scripts defer by nature.)

4.5 Priorities, limits, and hints

The browser does not fetch the dependency tree in document order; it fetches by priority. Render-blocking CSS and the fonts needed for visible text are high priority; an image far down the page or an async analytics script is low. The browser assigns these priorities automatically from the kind of resource and where it sits, and you can nudge them: the fetchpriority attribute raises or lowers a specific request, and the resource hints — preconnect to warm up a connection, preload to start a critical fetch early, prefetch to grab something for the next navigation — let the author correct the browser's guesses. These are how you fix the “font discovered late” problem: preload it so it does not wait for the CSS.

The browser is a scheduler for the network. Priorities decide what the user sees first; hints are how the author argues with the defaults.

4.6 The critical rendering path

Out of the whole dependency tree, only a subset is required before the browser can show anything: the HTML, the render-blocking CSS, and any parser-blocking JavaScript ahead of the visible content. That subset is the critical rendering path, and shortening it — less blocking CSS, deferred scripts, preloaded essentials — is the most direct lever on how quickly a page paints. Everything outside the critical path (below-the-fold images, async scripts, non-essential fonts) can arrive afterward without delaying first paint. Demo Company's critical-render-path page exists to let you watch a single synchronous script sit squarely in that path and hold the whole page hostage until it loads and runs.

This is the bridge to Module 5. Once the critical resources are in — the DOM from Module 3 and the CSSOM built from the render-blocking CSS — the browser has everything it needs to compute styles, lay out boxes, and paint. The resource model decides when the pixel pipeline can start.

4.7 When dependencies fail

The network is not reliable terrain, and a dependency tree is only as robust as its handling of missing branches. A resource can be slow, can 404, can hang, can be blocked. A well-built page degrades rather than breaks: a failed image leaves its alt text and reserved space rather than collapsing the layout; a font that does not arrive falls back to a system face via font-display; a failed enhancement script leaves working HTML behind it. This is progressive enhancement seen from the loader's side — build the critical path so that the non-critical failing does not take the page down with it.

4.8 The lab: watch the tree grow

Walk Demo Company's resource pages in order with the Network panel open, and read each new request against this module:

image.html + cat.webp a leaf: an image referenced straight from the DOM css.html + main.css render-blocking; holds first paint font.html + main.css → font a chain: the font hides inside the CSS javascript.html + app.js + cdn.js local and third-party scripts iframe.html + embedded document a whole subtree with its own resources

Sort the waterfall by start time and watch the parallelism; sort by the dependency and watch the chains. Find the font on the font page and confirm it begins only after main.css finishes — then imagine the preload that would fix it. You are looking at the resource model's own ledger.

4.9 What you now have

The page as a dependency tree rather than a file; discovery through parsing and the preload scanner racing ahead of blocked parsing; the waterfall as the view that exposes parallelism and chains; the blocking behaviors that matter most — render-blocking CSS and the plain/async/defer choice for scripts; priorities and hints as the controls; and the critical rendering path as the subset that gates first paint. The unifying idea: the browser is a scheduler for the network, and how it orders the fetch decides how fast the page feels.

With the critical resources in hand — the DOM and the CSSOM — the browser can finally turn structure and style into something on screen. Module 5 is the pixel pipeline: computing styles, laying out boxes, painting, and compositing layers on the GPU, and the reason some changes are cheap and others force the whole machine to run again.

You wrote one file. The browser fetched a forest. Now it has to draw it.