Anna Monus Anna is a technical writer who covers frontend frameworks, web standards, accessibility, WordPress development, UX design, and more. Head to her personal blog, Annalytic, for more content.

5 tricks to eliminate render blocking resources

8 min read 2326

5 Tricks to Eliminate Render Blocking Resources

Render blocking resources are static files, such as fonts, HTML, CSS, and JavaScript files, that are vital to the process of rendering a web page. When the browser encounters a render blocking resource, it stops downloading the rest of the resources until these critical files are processed. In the meantime, the entire rendering process is put on hold. On the other hand, non-render blocking resources don’t postpone the rendering of the page. The browser can safely download them in the background after the initial page render.

However, not all resources that the browser deems render blocking are essential for the first paint; it all depends on the individual characteristics of the page. There are best practices you can use to turn these noncritical render blocking resources into non-render blocking ones. Besides, you can also decrease the number and/or size of render blocking resources that are still critical and can’t be eliminated.

Why eliminate render blocking resources?

If you reduce the number of render blocking resources, you can shorten the critical rendering path and reduce page load times, thus improving the user experience and search engine optimization.

There are three ways to reduce the number and impact of render blocking resources:

  1. Make them non-render blocking resources by deferring their download
  2. Decrease the total number of render blocking resources using techniques such as bundling (this also means fewer HTTP requests)
  3. Reduce the size of a resource via minification so that the page has fewer bytes to load

Types of render blocking resources

As a rule of thumb, the browser treats everything it finds in the <head> section of an HTML page as render blocking. This includes:

  • CSS stylesheets
  • JavaScript files added in the <head> section
  • Fonts added from either CDN or a local server
  • HTML imports (even though HTML imports are now obsolete, you might still encounter them on legacy pages)

Images, media files, and <script> tags placed at the bottom of the <body> section are treated as non-render blocking resources.

Now let’s zoom in on five strategies to eliminate or reduce the number and impact of render blocking resources.

1. Don’t add CSS with the @import rule

You can add CSS to a page using either:

  • The <link rel="stylesheet"> tag that you need to add to your HTML file
  • The @import rule that you need to add to your CSS file

Even though the @import rule keeps your HTML file cleaner and allows you to keep all your CSS dependencies in the same place, it’s not the best choice performance-wise. The @import rule lets you import CSS from other stylesheets, but this causes the browser to process your CSS file more slowly because it also has to download the imported files. Until this takes place, the rendering process will be blocked.

If you want to add more than one CSS file to your page, you can either use the <link> tag or concatenate the files using a minification and/or bundling tool.

We made a custom demo for .
No really. Click here to check it out.

You need to add the <link> element to the <head> section of the HTML page in the following way:

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

2. Use the media attribute for conditional CSS

By default, the browser treats all CSS files as render blocking resources. However, if you add the media attribute to the <link> tag, you can indicate the presence of a conditional CSS file to the browser.

Conditional CSS only applies under certain conditions, such as below or above a given viewport size or on a print page. With the media attribute, you can define a specific media condition for a CSS file. You can use any value that you would use for a media query in a CSS file. For example:

<link href="print.css" rel="stylesheet" media="print">
<link href="large.css" rel="stylesheet" media="screen and (min-width: 1500px)">
<link href="mobile.css" rel="stylesheet" media="screen and (max-width: 600px)">

Even though these files are still download on all devices, they become non-render blocking resources if the condition evaluates as false. However, they will still be render blocking if the condition evaluates as true.

For instance, the mobile.css stylesheet in the above example will be render blocking on mobile devices with a maximum viewport width of 600px and non-render blocking on viewports larger than 600px.

If you have an existing CSS file with one or more media queries, you can extract all @media rules and save them as separate files using this PostCSS plugin. This performance optimization technique is also called code splitting. Although code splitting is usually mentioned in conjunction with JavaScript, you can also split larger CSS files and load each file only when needed to shorten the critical rendering path and decrease initial page load times.

3. Use the defer and async attributes to eliminate render blocking JavaScript

JavaScript files added to the <head> section of the document are treated as render blocking resources by default.

You can remove them from the critical rendering path by placing the <script> tags right before the closing </body> tag instead of the <head> section. In this case, they only begin to download after the entire HTML has been downloaded. However, because the download of these scripts starts later, elements loaded by them, such as ads, animations, or dynamic functionalities, might load later than the rest of the frontend — especially if it’s a longer script. This can result in noticeable delays and lagging UIs on slower connections, which is bad for the user experience.

The defer and async attributes of the <script> tag offer a solution to this problem. Both are Boolean attributes which means that if you add them, they will fire without any further configuration. They also make scripts added to the <head> section of an HTML document non-render blocking, but in a different way — deferred scripts respect the document order while asynchronous scripts are independent of the DOM.

The defer attribute instructs the browser to download the script in the background so it won’t block the rendering of the page. The deferred script executes once the DOM is ready but before the DOMContentLoaded event fires.

<script src="script01.js" defer></script>
<script src="script02.js" defer></script>

Deferred scripts follow the document order, just like nondeferred, default scripts. For example, in the above example, script01.js will be executed first, irrespective of which script loads first. You can’t add defer to inline scripts; it only works with external scripts that specify the script’s location using the src attribute.

On the other hand, the async attribute informs the browser that a script is completely independent of the page. It will download in the background as a non-render blocking resource, just like deferred scripts. However, unlike deferred scripts, async scripts don’t follow the document order, so they will execute whenever they finish downloading — which can happen at any time.

For instance, in the below example, we can’t be sure which script will run first; it solely depends on which downloads faster (usually the smaller one). Remember, async scripts are independent of both the document and each other, so the document order won’t impact them in any way.

<script src="script03.js" async></script>
<script src="script04.js" async></script>

The defer attribute is recommended for scripts that need the DOM, but you want to begin to download them before the document loads, without making them a render blocking resource. You should also use defer rather than async if the document order is important — for instance, when consecutive scripts depend on each other.

The async attribute is recommended for independent third-party scripts, such as ads, trackers, and analytics scripts. For example, Google Analytics recommends adding the async attribute to support asynchronous loading in modern browsers.

4. Minify and bundle CSS and JavaScript

In addition to removing nonessential CSS and JavaScript from the critical rendering path, you can also minify and bundle both render blocking and non-render blocking resources.

For instance, you can create bundles for files that use the same loading rules and minify each bundle separately. Since minified files are lighter and bundles mean fewer files in the critical rendering path, initial page rendering will finish sooner. Plus, it will also take less time to download non-render blocking resources in the background.

There are numerous tools available to help you perform minification and bundling according to best practices, including Minify, CSS Minifier, Minify Code, and PostCSS. Build tools such as webpack, Parcel, and Rollup come with built-in minification, bundling, and code splitting functionalities that enable you to quickly reduce the number of render blocking resources.

5. Load custom fonts locally

Since custom fonts are called from the <head> section of the document, they are also render blocking resources. For instance:

<link href="https://fonts.googleapis.com/css2?family=Lato&display=swap" rel="stylesheet"> 

You can reduce the impact of custom fonts on initial page rendering by adding them locally rather than pulling them from a content delivery network such as Google CDN. Font providers tend to add multiple @font-face rules, many of which you won’t need.

For example, Google Fonts add @font-face rules for all the character sets a typeface comes with, such as Latin, Cyrillic, Chinese, Vietnamese, and others. Let’s say, for instance, that the online CSS file you add with the <link> tag includes @font-face rules for seven different character sets, but you only want to use one (e.g., Latin). However, Google Fonts don’t download the font files for all the character sets; they just add many redundant @font-face rules to the CSS file.

If you add fonts locally, you can also minify your font-related CSS and bundle it together with the rest of your CSS. You can use the Google Web Fonts Helper to generate local @font-face rules for Google Fonts quickly. For instance, this is what you need to add to include the Lato Regular font face:

/* lato-regular - latin */
@font-face {
  font-family: 'Lato';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: local('Lato Regular'), local('Lato-Regular'),
       url('../fonts/lato-v16-latin-regular.woff2') format('woff2'),
       url('../fonts/lato-v16-latin-regular.woff') format('woff');
}

Note that Google Web Fonts Helper doesn’t add the font-display: swap rule; I added it myself to the above declaration. This is a descriptor of the @font-face rule that lets you specify how the browser should display the font face on the page.

By using font-display with the swap value, you instruct the browser to immediately begin to use a system font and swap it with the custom font once it downloads (this rule is also added when you pull the font from Google’s CDN). This enables you to avoid invisible text on the page while the custom font is still loading.

When you load fonts locally, make sure you serve compressed font formats for modern browsers, such as WOFF and WOFF2. Remember that lighter files reduce the impact of render blocking resources too. In addition to generating the @font-face rules, Google Web Fonts Helper also lets you download a zipped file that contains all the font formats you need.

Why you shouldn’t load custom fonts asynchronously

Some articles about render blocking resources recommend using TypeKit’s Web Font Loader to load custom fonts asynchronously. It was a decent tool once upon a time, but it hasn’t been updated since 2017 and it has many unresolved issues. I wouldn’t recommend using it.

Although loading fonts asynchronously shortens the critical rendering path, you should always do it carefully. If fonts load later than the page content, the page can produce a common UX problem called flash of invisible text (FOIT).

There are various ways to handle FOIT, such as using third-party libraries or the aforementioned font-display: swap rule (see browser support for font-display, plus note that using it with the swap value just turns FOIT into FOUT — flash of unstyled text — but doesn’t completely eliminate the issue). Still, you’ll want to spend time considering whether it’s really worth going the async route performance-wise. Think of the weight of extra scripts, potential issues, users with disabled JavaScript (you still need to add the static <link> element within <noscript> tags to support them), etc.

Summary

In this article, we discussed a five strategies to eliminate render blocking resources. To summarize:

  1. Don’t use CSS imports
  2. Load conditional CSS with media attributes
  3. Use the defer and async attributes to eliminate render-blocking JavaScript
  4. Split, bundle, and minify CSS and JavaScript files
  5. Load custom fonts locally

To locate your render blocking files, you can use performance analytics tools such as Lighthouse, web.dev (the same tool as Lighthouse, just integrated into a web app), GTmetrix, and others. In addition to helping you find render blocking resources, these tools offer hands-on tips to help you improve performance on your site.

The list of best practices discussed in this article are nonexhaustive. For instance, it’s also a good idea to run regular website audits to detect and remove unused code, which can have a huge impact on initial page rendering times.

To improve overall page load times, you can also use resource hints and the preload directive. They don’t eliminate render blocking resources per se, but you can use them to improve page load times. Render blocking resources won’t stop the fetching process of preloaded resources, and you can also preconnect to Google CDN to make web fonts load faster if you don’t want to load them locally.

For an in-depth guide to browser rendering, check out “How browser rendering works — behind the scenes.”

You come here a lot! We hope you enjoy the LogRocket blog. Could you fill out a survey about what you want us to write about?

    Which of these topics are you most interested in?
    ReactVueAngularNew frameworks
    Do you spend a lot of time reproducing errors in your apps?
    YesNo
    Which, if any, do you think would help you reproduce errors more effectively?
    A solution to see exactly what a user did to trigger an errorProactive monitoring which automatically surfaces issuesHaving a support team triage issues more efficiently
    Thanks! Interested to hear how LogRocket can improve your bug fixing processes? Leave your email:

    : Debug JavaScript errors easier by understanding the context

    Debugging code is always a tedious task. But the more you understand your errors the easier it is to fix them.

    LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to find out exactly what the user did that led to an error.

    LogRocket records console logs, page load times, stacktraces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!

    .
    Anna Monus Anna is a technical writer who covers frontend frameworks, web standards, accessibility, WordPress development, UX design, and more. Head to her personal blog, Annalytic, for more content.

    Leave a Reply