CS Chris Smith
CodePen Mastodon X (formerly Twitter) Feed

UI Performance - Critical Rendering Path Optimisation

by Chris Smith

A quick guide to how a web page renders and how we can ensure it stays fast. We tend to focus on load times and how quickly our requests receive responses. This focuses on what happens after that and how we keep the experience smooth for users.

How a page renders


HTML is parsed to create the DOM (Document Object Model, a tree structure).

CSS is parsed and all of the cascade, custom properties and calculations are done to arrive at final values. This creates the CSSOM (Cascading Stylesheet Object Model, another tree structure).

The DOM and CSSOM are combined into the Render Tree. This decides what is rendered and how it is styled, e.g. an element with display: none will not be rendered.

Next comes Layout. The browser has to calculate the exact size and position of every rendered element, and then which fit into the viewport.

Now Paint. Now that the browser knows the exact size, position and styling of every element they're created as layers of rasterised images. As HTML and CSS are needed to reach this point these are always render blocking resources.

Finally, there's a Composite step where these paint layers are brought together and this gives the rendered page.

So, in terms of work for the browser, we've got these 6 steps:

  1. HTML > DOM
  2. CSS calculations > CSSOM
  3. Render tree (DOM + CSSOM)
  4. Layout calculations
  5. Paint layers
  6. Composite

This process of getting to the first render is called the Critical Rendering Path.


Fonts are requested after the Render Tree, once the browser knows which font characters are needed. This can block rendering or, at least, the rendering of text until the font has loaded. By preloading the font in the <head> we can load it instantly from the cache when the time comes to use it.


When the browser hits a <script> tag it stops the rendering process until the script has finished executing. As JavaScript can alter the DOM or CSSOM the browser pauses building, known as "render blocking".

JavaScript cannot run until the CSSOM is built so will wait for it before executing.

Adding the async property to a <script> tag means it will download in parallel with the document and execute as soon as it is available.

Adding the defer property to a <script> tag means it will download in parallel with the document but only execute once the DOM and CSSOM have been created. It executes before the DOMContentLoaded event is fired. JavaScript modules are deferred automatically.

If JavaScript alters the DOM or CSSOM then the browser has to go through some of the rendering steps again.


Any render blocking slows things down. This can be HTML or CSS that is not needed for the initial page load or JavaScript that causes rendering to wait.

JavaScript that uses an internet connection, e.g. a fetch(), slows things down much more as the page has to wait for the response and the script execution to finish before rendering can continue.

Trying to run scripts before all CSS has loaded and created the CSSOM results in scripts having to wait. Having large or multiple stylesheets increases this delay.

If JavaScript loads CSS (common in packages) then the render has to wait for the JavaScript to finish executing, which in turn has to wait for the CSSOM to update. It's starting to queue up.

The most common issue (in my opinion) is trying to load everything up front rather than using a well thought-out strategy to load content progressively as it is needed - designing for performance.

Critical Rendering Path (CRP) Optimisation

A Critical Rendering Path optimisation strategy enables a page to load as quickly as possible by prioritising resources and the order in which they are loaded.


A user's perception of an element moving is based on the speed at which the screen re-renders, the frame rate, measured in frames per second (fps). If an element jerks from one position to the next the illusion is broken, so it's important we can always re-render fast enough to avoid this.

User Interactions

When a user interacts with the UI, e.g. pressing a button or typing in a search box, then the UI needs to respond quickly so that the user does not feel a disconnect and that the UI is unresponsive. Ideally we should always acknowledge their interaction within 50ms. This does not have the be the completion of the action but we do need to confirm their interaction visually so that they don't question it, e.g. did they definitely press the button?


Useful Links

Critical Rendering Path | Web Fundamentals | Google Developers

Preload critical assets to improve loading speed (web.dev)

Optimize WebFont loading and rendering