Understanding the Critical Rendering Path

In Brief
The browser turns HTML into the DOM and CSS into the CSSOM, combines them into a render tree, calculates layout, and paints pixels. Blocking stylesheets and parser‑blocking scripts can delay that first render. Treat this as a useful model, not a literal recipe for every browser. Measure the dependency chain, then remove, defer, or reduce work that the initial view does not need.
A page can be small and still leave the browser staring at a blank screen. The HTML arrived quickly, the stylesheet is only a few kilobytes, and the JavaScript is not doing much. The delay comes from the order in which those files are discovered, downloaded, and processed before the browser has enough information to draw anything useful.
That sequence is the critical rendering path.
From Source Files to Pixels
As the browser reads HTML, it tokenises the markup and builds the Document Object Model, or DOM. The DOM describes the document's nodes and their relationships. It is not a picture of the page.
CSS goes through a related process. The browser parses the applicable stylesheets into the CSS Object Model, or CSSOM. Once it has the DOM and the styles needed for rendering, it can work out which nodes will be visible and how they should be styled. That produces a render tree.
The remaining work is usually described in two broad stages. Layout calculates the geometry of the boxes: their sizes and positions. Paint turns those boxes, text, colours, borders, and images into pixels. Browsers may split painted output into layers and composite them, but DOM, CSSOM, render tree, layout, and paint are the useful starting model.
HTML -> DOM ------\ > Render tree -> Layout -> PaintCSS -> CSSOM ----/This is not a claim that every engine runs five neat functions once. Browsers parse progressively, work ahead, and repeat parts of the pipeline as resources arrive or the page changes. The critical rendering path is the dependency chain that must complete for the initial render, not a complete description of the rendering engine.
CSS Blocks Rendering for a Reason
The browser cannot safely paint a styled page until it knows which CSS applies. A stylesheet in the document head is therefore a render‑blocking resource for the media in use:
<link rel="stylesheet" href="/css/site.css">That does not make stylesheets bad. It means their discovery time, transfer size, and processing cost affect when the first render can happen. If the main stylesheet imports another stylesheet, the browser discovers the second dependency only after downloading and parsing the first. The bytes may be modest, but the dependency chain has gained another network round trip.
Keep the CSS needed by the initial view easy to discover. Remove unused rules where practical, compress the file, and avoid long @import chains. A print stylesheet can declare media="print" so it does not block rendering for the screen:
<link rel="stylesheet" href="/css/print.css" media="print">Inlining a small amount of critical CSS can remove a request from the path. The word "small" is doing real work there. Inlining the whole site stylesheet increases every HTML response, prevents the browser caching that CSS as a separate resource, and creates two copies to keep in step if the rest is loaded later.
Classic Scripts Can Stop the Parser
A classic external script without async or defer blocks HTML parsing whilst it is fetched and executed:
<script src="/js/vendor.js"></script><script src="/js/app.js"></script>The browser stops because the script may call document.write(), inspect the DOM built so far, or change the document. If a stylesheet appears before that script, the script may also need to wait for the stylesheet because it can ask questions whose answers depend on CSS.
This produces a dependency chain that is easy to miss when files are considered separately. HTML discovers CSS. A script waits behind that CSS. Parsing resumes after the script. The parser cannot reach a later image or stylesheet in the meantime, although a browser's speculative loader may discover some URLs ahead of it.
Move a script later in the document when it genuinely needs the preceding markup but not the document head. For scripts that can be downloaded without blocking the parser, choose between async and defer according to their dependencies:
<script src="/js/analytics.js" async></script><script src="/js/app.js" defer></script>An asynchronous script downloads in parallel and executes as soon as it is ready. Its order relative to other asynchronous scripts is not guaranteed, so it suits independent code such as analytics. A deferred script also downloads in parallel, but waits until the document has been parsed and retains order relative to other deferred scripts. It suits application code that needs the completed DOM or another deferred file.
Adding async to every script is not an optimisation strategy. A script that depends on a library loaded beside it can simply run first and fail faster.
Count Dependencies, Not Just Kilobytes
The original critical rendering path guidance describes three useful measurements: the number of critical resources, the critical bytes they contain, and the length of the critical path.
The third is often the revealing one. Two files may download in parallel after the HTML exposes them, whilst another is hidden behind an import or a parser‑blocking script. Reducing 20 KB from a file is useful, but removing a whole serial dependency can save a round trip as well as bytes.
Sketch the chain for the first useful view:
document.html |-- site.css | `-- fonts.css `-- vendor.js `-- app.js inserted after vendor.js runsThen ask which resources are genuinely required before anything useful can appear. A decorative image, an analytics script, and styles for a component below the fold may be valuable without being critical.
Measure the Page You Actually Ship
Chrome's Network panel shows discovery order, request timing, initiators, and the waterfall. Preserve the log, disable the cache for a clean first‑load recording, and throttle the connection if the fast local network hides dependencies. Do not read the bars as independent stopwatch results. Their position tells you what had to happen before each request could begin.
Use the Performance panel to record a reload and inspect parsing, script execution, style calculation, layout, and paint. Chrome called this the Timeline panel in earlier releases; it was renamed to Performance in Chrome 58. The labels vary between browser versions, but the question stays the same: what occupied the path before the first useful paint?
Change one dependency at a time and record again. Moving a script, removing an import, or inlining a small critical rule set should alter the waterfall for a reason you can explain. If the chart improves but the page flashes unstyled content or initialises in the wrong order, the dependency was not safely removed.
The broader guide to optimising website performance covers the rest of the page's weight and runtime work. Once rendering has begun, repeated DOM reads and writes can create forced layout and layout thrashing, but that is a later problem than arranging the initial dependency path. Scroll handlers have their own runtime concern, covered by passive event listeners.
The Useful Part of the Model
The critical rendering path turns a vague complaint about a slow first view into a dependency problem. The browser needs a DOM, the applicable CSSOM, and enough completed work to create, lay out, and paint the initial render. Every blocking resource on that route needs a reason to be there.
Keep necessary resources early and small. Move independent work out of the path with the right loading behaviour. Most importantly, use the waterfall and rendering record to prove which dependency delayed the pixels. A blank screen is an outcome, not a diagnosis.
Postscript
Aug 2026: This article forms part of an archive restored from a previous version of my website. Its original publication date is accurate. During the restoration, I reviewed and updated it where appropriate for formatting, imagery, broken links, code correctness, and current internal references, whilst preserving the original technical context and intent.
The rendering pipeline remains relevant, although performance metrics, browser tooling, and loading features have developed considerably since 2017. The historical body deliberately uses the terminology and techniques available at the stated publication date. The later Core Web Vitals regression runbook provides one current measurement path.