Web Performance: Understanding the Critical Render Path (Part 1)

The biggest turnoff when visiting a new web application can be slow load times!

Omer Goldberg
HackerNoon.com
Published in
6 min readJul 1, 2019

--

Luckily, there are a several techniques to speed up loading times considerably. Before we learn optimization we will need to understand how the browser loads a page. Remember the golden rule of optimizations:

Measure First, Optimize Second

I would add an extra step before measuring which is understanding :) This will allow us to develop a strong intuition for things that may be hindering our websites performance early on.

What is the Critical Render Path?

The critical render path is the sequence of steps the browser goes through to convert HTML, CSS and Javascript into actual pixels on the screen. Once we understand the Critical Render Path we will be able to improve load times in websites. Let’s break down this name before diving in:

  • critical-necessary to load
  • render-display in users browser
  • path-sequence of events leading to our page showing in users browser

Critical Render Path Sequence

Before we dive into the details let’s examine the sequence in whole:

  1. DOM: We process the HTML by characters, tokenising and creating DOM nodes which will ultimately be part of the DOM.
  2. CSSOM: Once we process the head and we fetch the requested CSS resources we start building the CSSOM. This part is blocking as we will see below.
  3. Building the Render Tree 🌳 🌲: We combine the created DOM and CSSOM to create the Render Tree.
  4. Layout: Calculating the layout of the page by examining the created Render Tree.
  5. Paint: We can finally paint the pixels on the screen.

Building the Document Object Model (DOM)

So how does the browser turn a request into the pixels we see on the screen?

example HTML payload
  1. Browser receives an HTML payload in byte format.
  2. We begin by scanning for characters to identify tokens.
  3. Once tokens are identified a separate process begins consuming tokens and creating DOM nodes.
  4. The relationship between the nodes is defined by start (<>) and end(</>) tokens which let us define parent — children 👶 relationships between nodes.

All together the DOM parsing sequence looks like this:

Once we consume all of the tokens and create their respective nodes we arrive at a tree like structure called the DOM. See image below for an example of how HTML is translated into the DOM:

From Udacity

Building the DOM is a render blocker, since obviously without the DOM we wouldn’t have anything to render.

Building the CSS Object Model (CSSOM)

The DOM captures the content of the page but we also need to know how to display the page itself. For that we need the CSSOM. The process of parsing and building the CSSOM is very similar to that of the DOM.

Great, so we read the parsed CSS rules received and map them onto the nodes we have.

Mapping styles to HTML elements

The mapping algorithm is naive. We search for the node with the corresponding identifier. More specific CSS rules take longer to find and apply as we read rules from right to left. Let’s looks at this example - which rule do you think will take longer to apply?

The first rule says modify any <h1> tag to have a font-size of 16px.

The second rule (which is read from right to left) says find a <p> tag. For every <p> tag search for a <div> ancestor. If such exists apply font-size 16px. Clearly, this rule is more complicated and takes longer to find as we are traversing more DOM nodes in the tree. Having said that, the difference is likely trivial and not a large bottleneck in performance.

Surprisingly, building the CSSOM is also a render blocking resource for the browser. This is because many websites are basically unusable until the CSS is applied. For example, look at the difference of my portfolio website with and without CSS.

Although CSS is a render blocker, media types and queries allow us to mark some CSS resources as non-render blocking. Declaring our media types specifically can really improve load times (we’re saving unnecessary, fetches and parses!) so pay attention to this with your CSS includes.

Let’s look at this example that can be found here:

From the Google Documentation
  • The first two CSS links are equivalent. They will be applied in every scenario, regardless of action or screen size.
  • The third CSS include will only apply if the screen’s orientation is set to portrait.
  • The fourth CSS include will only apply if we are printing the document.
  • Specifying the last two media types has made these resources non-blocking, thus our site will load faster in scenarios where these are not applicable.

Also note, that “render blocking” in the context of CSS only refers to whether the browser has to pause the rendering of the page until that resource is received. In any case the browser will always download all CSS resources, regardless of blocking or non-blocking behavior.

Given the blocking nature of CSS we will always strive to get CSS to the client (users browser) as soon as possible to improve the initial render time.

Render Tree

The Render Tree combines the DOM and the CSSOM, and only contains the content that is visible on the screen 📺 . Therefore we won’t see any elements in the render tree that have display set to none.

Let’s see how the DOM and CSSOM combined create a Render Tree:

When the render tree is complete we can continue to the layout phase.

Layout

We have the render tree but we still need to figure out how the elements should be positioned on the screen. To figure out the exact size and position of each object on the page, the browser looks at the root of the received Render Tree and begins traversing it.

Sizing elements and placing them is always relative to the parent divs, unless specified otherwise. Let’s take a look:

From Udacity

The time for the layout phase is dependant on several factors:

  • device it is running on
  • size of the document
  • applied styles to document

The larger the document it is and the more complicated the styling is, the longer it will take to complete the layout phase.

Paint 🎨🎨

At this point we know which nodes are visible, and their computed layout and styles. In this step, we take the final render tree and render the pixels (individual nodes) to the screen 😱😱.

Summary

We have learnt a lot here. Let us summarize the steps needed to render a web page received via a HTTP GET request from the browser.

  1. Parse and process HTML and build the DOM.
  2. Parse and process CSS and build the CSSOM.
  3. Merge DOM and CSSOM for the render tree.
  4. Running layout calculations to compute size and position of each node in Render tree.
  5. Paint each node (pixel) on the screen.

In order to make our pages load super fast we will aim to make each step in the above process as fast as possible!

The next post in this series we will look at Critical Render Path optimizations. Remember, we can’t optimize what we can’t measure!

If this post was helpful, please subscribe and click the clap 👏 button below to show your support! ⬇⬇

You can follow me on Instagram, Linkedin , and Medium for more tech related content!

--

--