---
title: 'Eliminating Render Blocking CSS and measuring page rendering time with Performance API'
date: '2023-03-11'
lastmod: '2024-10-18'
tags: ['render-blocking-resources', 'render-blocking-css', 'performance', 'chrome-performance-api']
summary: "Learn how to improve your website's performance and enhance user experience by avoiding render blocking CSS. Using the Chrome Performance API to measure and optimize page rendering time."
images:
  [
    '/static/images/banners/graphs-of-performance-analytics-on-a-laptop-screen__lukechesser__JKUTrJ4vK00.jpeg',
  ]
authors: ['default']
---

**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](/static/images/render-blocking-css.png)

<div class="pt-2 text-center italic">
  <small>CSS greatly affects page load time</small>
</div>
<br />

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:

```html showLineNumbers
<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](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-media).

```html showLineNumbers
<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](https://developer.mozilla.org/en-US/docs/Web/CSS/@media#Description) or [media query](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_queries), 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](/static/images/inline-css.png)

<div class="pt-2 text-center italic">
  <small>One of the techniques to help Gatsby site load extremely fast is Inline CSS</small>
</div>

## Performance API

Now let's measure **page rendering time** after applying some techniques to avoid **Render Blocking CSS** using the [Chrome Perfomance API](https://developer.mozilla.org/en-US/docs/Web/API/Performance).

I have provided a simple example to help you understand **Render Blocking CSS** here: [https://hta218.github.io/render-blocking-css-example/](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`.

```html showLineNumbers
<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**.

```js showLineNumbers
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](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceEntry), and in this case, I used [PerformancePaintTiming](https://developer.mozilla.org/en-US/docs/Web/API/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](/static/images/css-block-render.png)

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](/static/images/css-not-block-render.png)

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](/static/images/render-result.png)

Alternatively, you can use the [PerformancePaintTiming API](https://developer.mozilla.org/en-US/docs/Web/API/PerformancePaintTiming) directly.

```js showLineNumbers
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

- [Eliminate render-blocking resources](https://web.dev/render-blocking-resources/)
- [Render Blocking CSS](https://developers.google.com/web/fundamentals/performance/critical-rendering-path/render-blocking-css)
- [https://varvy.com/pagespeed/render-blocking-css.html](https://varvy.com/pagespeed/render-blocking-css.html)
- [Chrome Performance API](https://developer.mozilla.org/en-US/docs/Web/API/Performance), [PerformanceObserver](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver/PerformanceObserver), [PerformancePaintTiming](https://developer.mozilla.org/en-US/docs/Web/API/PerformancePaintTiming)
