Leo's dev blog

Eliminating Render Blocking CSS and measuring page rendering time with Performance API

Graphs of performance analytics on a laptop screen
Published on
/6 mins read/---

CSS is considered by browsers as one of the render-blocking resources - resources that your page must load before users can see the content.

Why should you avoid Render Blocking CSS?

  • Render blocking CSS will slow down the display of your website to users.

  • Each CSS file that your website loads adds to the first-paint time of the page, meaning users have to wait longer to see content if your page has to load a lot of CSS.

render-blocking-css
CSS greatly affects page load time

When starting to load a page, the browser automatically loads all CSS files, regardless of whether they block the rendering process or not!

So, how can you limit render-blocking CSS?

Solutions

If you notice that your page has CSS that is only used in certain conditions, such as styles for content inside a modal that the user must click to open and view, content inside a tab that is not displayed first, or styles that only apply to large monitors or mobile devices...

Here are some ways to help your page load faster.

Use the media attribute

When you want to load CSS into your web page, you will use the link tag like this:

<link href="style.css" rel="stylesheet" />
<link href="print.css" rel="stylesheet" />
<link href="style.mobile.css" rel="stylesheet" />

After loading the HTML, the browser will also load these 3 CSS files and only display the content after all of them have loaded.

However, print.css is only used when printing a document (Ctrl/Cmd + P), and style.mobile.css is only for styles that apply on mobile devices.

In this case, we can use the media attribute.

<link href="style.css" rel="stylesheet" />
<link href="print.css" media="print" rel="stylesheet" />
<link href="style.mobile.css" media="(max-width: 568px)" rel="stylesheet" />

Now the browser understands that it only needs to load the style.css file to display the page content immediately to the user, without waiting for the other 2 files to load.

For links that use the media attribute:

  • media="print": the styles in this file will only apply when printing a document, so there is no need for rendering and it will not block the first render when loading the page.
  • media="(max-width: 568px)": these styles only apply on devices with max-width=568px, and will not block the first render when loading the page on a desktop/tablet device.

Using the media attribute, we can adjust the display of the page in specific cases such as after rendering, adjusting screen size, changing device orientation (horizontal/vertical)...

The value of media must be a media type or media query, which is very useful when loading external stylesheets - it helps the browser choose the necessary CSS for the first render.

Combine css or inline css

One effective way is to directly place the CSS in a style tag in the document header if the CSS is not too large. This method improves performance very well as it only needs to be displayed as soon as the DOM is loaded.

Or you can limit the loading of too many CSS files. Usually when coding, developers tend to separate different CSS files by component, module, etc. for easy management. However, loading many CSS files takes longer than loading only one file.

inline-css
One of the techniques to help Gatsby site load extremely fast is Inline CSS

Performance API

Now let's measure page rendering time after applying some techniques to avoid Render Blocking CSS using the Chrome Perfomance API.

I have provided a simple example to help you understand Render Blocking CSS here: https://hta218.github.io/render-blocking-css-example/

In the example, I loaded two CSS files and compared the page rendering time on screens with device-width larger and smaller than 800px.

<link rel="stylesheet" href="tailwind.css" media="(min-width: 800px)" />
<link rel="stylesheet" href="bootstrap.css" media="(min-width: 800px)" />

To use the Performance API, it is important to check browser compatibility.

if ('PerformanceObserver' in window) {
  try {
    // Create PerformanceObserver instance
    let perfObsever = new PerformanceObserver((perf) => {
      let perfEntries = perf.getEntriesByType('paint')
      perfEntries.forEach(({ name, startTime }) => {
        // Get the result inside this callback
        console.log(`The time to ${name} was ${startTime} milliseconds.`)
      })
    })
 
    // observe "paint" event
    perfObsever.observe({ entryTypes: ['paint'] })
  } catch (err) {
    console.error(err)
  }
} else {
  // Remember to check the browser compatibility before using this API
  console.log("Performance API isn't supported!")
}

There are many different Performance metric, and in this case, I used PerformancePaintTiming.

To see the results more clearly, you can open Chrome DevTools, throttle the internet, and CPU speed to simulate the conditions of the user's device.

For screens >800px (loading all CSS before the first page render),

css-block-render

We need more than 6 seconds for the first paint (after all CSS is loaded) - which is equivalent to the user waiting 6 seconds to see the content of the page.

For the case where only one CSS file is needed for the first paint (the remaining files are still loaded but not used or needed for the first render)

css-not-block-render

The user sees the content 2 seconds earlier and can see the page rendering even when the other 2 CSS files have not finished loading.

The results measured with Performance API are summarized below.

render-result

Alternatively, you can use the PerformancePaintTiming API directly.

if (window.performance) {
	let performance = window.performance;
	let perfEntries = performance.getEntriesByType('paint');
 
	perfEntries.forEach({ name, startTime } => {
		console.log(`The time to ${name} was ${startTime} milliseconds.`);
	});
} else {
	console.log("Performance timing isn't supported.");
}

Conclusion

We can clearly see the significant difference in page rendering time, which directly affects the user experience when the webpage has to load too much unnecessary CSS. Therefore, we need to be very careful about this resource.

There are many ways to avoid Render Blocking CSS, such as using media types or media queries, combining CSS, and inline CSS. These changes may seem small, but they have a significant impact on performance.

Refs