Oops! Something went wrong while submitting the form.
We use cookies to improve your browsing experience on our website, to show you personalised content and to analize our website traffic. By browsing our website, you consent to our use of cookies. Read privacy policy.
In the previous blog, we learned how a browser downloads many scripts and useful resources to render a webpage. But not all of them are necessary to show a page’s content. Because of this, the page rendering is delayed. However, most of them will be needed as the user navigates through the website’s various pages.
In this article, we’ll learn to identify such resources and classify them as critical and non-critical. Once identified, we’ll inline the critical resources and defer the non-critical resources.
For this blog, we’ll use the following tools:
Google Lighthouse and other Chrome DevTools to identify render-blocking resources.
Webpack and CRACO to fix it.
Demo Configuration
For the demo, I have added the JavaScript below to the <head></head> of index.html as a render-blocking JS resource. This script loads two more CSS resources on the page.
Lazy loading and code splitting using Suspense, React lazy, and dynamic import
CRACO
html-critical-webpack-plugin
ngrok and serve for serving build
Render-Blocking Resources
A render-blocking resource typically refers to a script or link that prevents a browser from rendering the processed content.
Lighthouse will flag the below as render-blocking resources:
A <script></script>tag in <head></head>that doesn’t have a defer or asyncattribute.
A <link rel="”stylesheet”">tag that doesn’t have a mediaattribute to match a user's device or a disabledattribute to hint browser to not download if unnecessary.
A <link rel="”import”"> that doesn’t have an asyncattribute.
Identifying Render-Blocking Resources
To reduce the impact of render-blocking resources, find out what’s critical for loading and what’s not.
To do that, we’re going to use the Coverage Tabin Chrome DevTools. Follow the steps below:
1. Open the Chrome DevTools (press F12)
2. Go to the Sources taband press the keys to Run command
The below screenshot is taken on a macOS.
3. Search for Show Coverage and select it, which will show the Coverage tab below. Expand the tab.
4. Click on the reload button on the Coverage tab to reload the page and start instrumenting the coverage of all the resources loading on the current page.
5. After capturing the coverage, the resources loaded on the page will get listed (refer to the screenshot below). This will show you the code being used vs. the code loaded on the page.
The list will display coverage in 2 colors:
a. Green (critical) - The code needed for the first paint
b. Red (non-critical) - The code not needed for the first paint.
After checking each file and the generated index.html after the build, I found three primary non-critical files -
a. 5.20aa2d7b.chunk.css - 98% non-critical code
b. https://use.fontawesome.com/3ec06e3d93.js - 69.8% non-critical code. This script loads below CSS -
c. main.6f8298b5.chunk.css - 58.6% non-critical code
The above resources satisfy the condition of a render-blocking resource and hence are prompted by the Lighthouse Performance report as an opportunity to eliminate the render-blocking resources (refer screenshot). You can reduce the page size by only shipping the code that you need.
Solution
Once you’ve identified critical and non-critical code, it is time to extract the critical part as an inline resource in index.html and deferring the non-critical part by using the webpack plugin configuration.
For Inlining and Preloading CSS:
Use html-critical-webpack-plugin to inline the critical CSS into index.html. This will generate a <style></style> tag in the <head> with critical CSS stripped out of the main CSS chunk and preloading the main file.</head>
Use lazy-loading and code-splitting techniques along with webpack’s magic comments as below to preload or prefetch a route/page according to your use case.
The defer and async attributes can be specified on an external script. The async attribute has a higher preference. For older browsers, it will fallback to the defer behaviour.
If you want to know more about the async/defer, read the further reading section.
Along with defer/async, we can also use media attributes to load CSS conditionally.
It’s also suggested to load fonts locally instead of using full CDN in case we don’t need all the font-face rules added by Font providers.
Now, let’s create and deploy the build once more and check the results.
The opportunity to eliminate render-blocking resources shows no more in the list.
We have finally achieved our goal!
Final Thoughts
The above configuration is a basic one. You can read the libraries’ docs for more complex implementation.
Let me know if this helps you eliminate render-blocking resources from your app.
If you want to check out the full implementation, here’s the link to the repo. I have created two branches—one with the problem and another with the solution. Read the further reading section for more details on the topics.
Eliminate Render-blocking Resources using React and Webpack
In the previous blog, we learned how a browser downloads many scripts and useful resources to render a webpage. But not all of them are necessary to show a page’s content. Because of this, the page rendering is delayed. However, most of them will be needed as the user navigates through the website’s various pages.
In this article, we’ll learn to identify such resources and classify them as critical and non-critical. Once identified, we’ll inline the critical resources and defer the non-critical resources.
For this blog, we’ll use the following tools:
Google Lighthouse and other Chrome DevTools to identify render-blocking resources.
Webpack and CRACO to fix it.
Demo Configuration
For the demo, I have added the JavaScript below to the <head></head> of index.html as a render-blocking JS resource. This script loads two more CSS resources on the page.
Lazy loading and code splitting using Suspense, React lazy, and dynamic import
CRACO
html-critical-webpack-plugin
ngrok and serve for serving build
Render-Blocking Resources
A render-blocking resource typically refers to a script or link that prevents a browser from rendering the processed content.
Lighthouse will flag the below as render-blocking resources:
A <script></script>tag in <head></head>that doesn’t have a defer or asyncattribute.
A <link rel="”stylesheet”">tag that doesn’t have a mediaattribute to match a user's device or a disabledattribute to hint browser to not download if unnecessary.
A <link rel="”import”"> that doesn’t have an asyncattribute.
Identifying Render-Blocking Resources
To reduce the impact of render-blocking resources, find out what’s critical for loading and what’s not.
To do that, we’re going to use the Coverage Tabin Chrome DevTools. Follow the steps below:
1. Open the Chrome DevTools (press F12)
2. Go to the Sources taband press the keys to Run command
The below screenshot is taken on a macOS.
3. Search for Show Coverage and select it, which will show the Coverage tab below. Expand the tab.
4. Click on the reload button on the Coverage tab to reload the page and start instrumenting the coverage of all the resources loading on the current page.
5. After capturing the coverage, the resources loaded on the page will get listed (refer to the screenshot below). This will show you the code being used vs. the code loaded on the page.
The list will display coverage in 2 colors:
a. Green (critical) - The code needed for the first paint
b. Red (non-critical) - The code not needed for the first paint.
After checking each file and the generated index.html after the build, I found three primary non-critical files -
a. 5.20aa2d7b.chunk.css - 98% non-critical code
b. https://use.fontawesome.com/3ec06e3d93.js - 69.8% non-critical code. This script loads below CSS -
c. main.6f8298b5.chunk.css - 58.6% non-critical code
The above resources satisfy the condition of a render-blocking resource and hence are prompted by the Lighthouse Performance report as an opportunity to eliminate the render-blocking resources (refer screenshot). You can reduce the page size by only shipping the code that you need.
Solution
Once you’ve identified critical and non-critical code, it is time to extract the critical part as an inline resource in index.html and deferring the non-critical part by using the webpack plugin configuration.
For Inlining and Preloading CSS:
Use html-critical-webpack-plugin to inline the critical CSS into index.html. This will generate a <style></style> tag in the <head> with critical CSS stripped out of the main CSS chunk and preloading the main file.</head>
Use lazy-loading and code-splitting techniques along with webpack’s magic comments as below to preload or prefetch a route/page according to your use case.
The defer and async attributes can be specified on an external script. The async attribute has a higher preference. For older browsers, it will fallback to the defer behaviour.
If you want to know more about the async/defer, read the further reading section.
Along with defer/async, we can also use media attributes to load CSS conditionally.
It’s also suggested to load fonts locally instead of using full CDN in case we don’t need all the font-face rules added by Font providers.
Now, let’s create and deploy the build once more and check the results.
The opportunity to eliminate render-blocking resources shows no more in the list.
We have finally achieved our goal!
Final Thoughts
The above configuration is a basic one. You can read the libraries’ docs for more complex implementation.
Let me know if this helps you eliminate render-blocking resources from your app.
If you want to check out the full implementation, here’s the link to the repo. I have created two branches—one with the problem and another with the solution. Read the further reading section for more details on the topics.
Velotio Technologies is an outsourced software product development partner for top technology startups and enterprises. We partner with companies to design, develop, and scale their products. Our work has been featured on TechCrunch, Product Hunt and more.
We have partnered with our customers to built 90+ transformational products in areas of edge computing, customer data platforms, exascale storage, cloud-native platforms, chatbots, clinical trials, healthcare and investment banking.
Since our founding in 2016, our team has completed more than 90 projects with 220+ employees across the following areas:
Building web/mobile applications
Architecting Cloud infrastructure and Data analytics platforms