How to Eliminate Render-Blocking Resources and Speed Up First Paint
Learn how to identify and eliminate render-blocking CSS and JavaScript that delay FCP and LCP. Covers critical CSS inlining, script defer, async loading, and third-party script handling.
Your PageSpeed Insights report flags "Eliminate render-blocking resources" as a high-impact opportunity. Below it, a list of CSS and JavaScript files with savings in the hundreds of milliseconds. You know it is important but the fix is not obvious — the stylesheets are there for a reason, and removing them breaks the page. This guide explains exactly what render-blocking resources are, why they delay your users, and how to eliminate them systematically without breaking your design.
What Render-Blocking Resources Are and Why They Matter
When a browser parses an HTML document, it builds a tree of elements. Before it can paint anything to the screen, it needs to know how each element should look — which requires processing all CSS. And if there is a synchronous script tag in the document, the browser pauses HTML parsing until that script finishes downloading and executing, because the script might modify the DOM.
The result: any CSS file in the link tag and any script without defer or async is a render-blocking resource. The browser cannot show the user anything until all blocking resources have been fetched, parsed, and processed.
On a page with four external CSS files and three synchronous scripts, those seven network requests happen sequentially before the first pixel appears. On a slow connection, this is the difference between a 1.0s FCP and a 4.0s FCP.
How to Eliminate Render-Blocking Resources
Step 1: Identify What Is Blocking
Open Chrome DevTools, record a Performance trace, and look at the main thread timeline during the initial load. Resources that block rendering appear as long tasks before the "First Contentful Paint" marker.
The Coverage tab (Cmd+Shift+P, search "Coverage") shows how much of each CSS and JavaScript file is actually used on the page during load. A stylesheet that is 90% unused on load is a strong signal that it contains non-critical styles that could be deferred.
PageSpeed Insights lists blocking resources explicitly in the "Eliminate render-blocking resources" audit. Start there.
Step 2: Handle Render-Blocking JavaScript
Scripts without defer or async block parsing. The fix is almost always adding one of these attributes.
defer: The script is downloaded in parallel with HTML parsing and executed after the HTML is fully parsed, in document order. Use for scripts that need to run in order and can wait until parse is complete.async: The script is downloaded in parallel with HTML parsing and executed as soon as it downloads, interrupting parsing. Use for scripts that are completely independent (analytics, tracking).
For the vast majority of third-party scripts — Google Analytics, tag managers, chat widgets, A/B testing tools — defer or async is appropriate. The only scripts that legitimately need to block are those that modify the DOM before any rendering occurs and cannot be deferred.
As a rule: if you did not explicitly decide a script needs to be blocking, it should have defer.
Step 3: Handle Render-Blocking CSS
CSS is more nuanced than JavaScript. The browser needs CSS to render correctly, so you cannot simply defer all of it. The strategy is to split CSS into critical and non-critical, inline the critical portion, and defer the rest.
Critical CSS is the minimal set of styles needed to render the above-the-fold content correctly on first paint. Everything needed for your header, hero, and visible text. Nothing needed for the footer, off-screen modals, or below-fold sections.
Inlining critical CSS means placing those styles in a style block in the HTML head instead of in an external file. The browser reads them immediately without an additional network request.
Deferring non-critical CSS means loading the main stylesheet without blocking rendering. The technique:
link rel="stylesheet" href="/styles.css" media="print" onload="this.media='all'"
noscript: link rel="stylesheet" href="/styles.css"The media="print" attribute tells the browser this stylesheet applies only to print, so it does not block screen rendering. The onload handler swaps it to media="all" once it finishes loading. A noscript fallback ensures it still loads for users without JavaScript.
Tools that automate critical CSS extraction: critical (npm package), Penthouse, and build-tool plugins for Webpack and Vite. These crawl your pages, identify above-the-fold CSS, and extract it automatically.
Step 4: Load Third-Party Resources Efficiently
Third-party resources are often the biggest render-blocking offenders because you cannot modify the third-party code. Strategies:
Preconnect to critical third-party origins: If you know you will load resources from a third-party domain, preconnect establishes the DNS, TCP, and TLS connection early:
rel="preconnect" href="https://fonts.googleapis.com"
This does not eliminate the blocking, but reduces its duration.
Use a tag manager with asynchronous loading: Google Tag Manager loads asynchronously by design and manages third-party scripts through a single asynchronous container. If your tag manager loads synchronously or if individual tags are configured to fire synchronously, that is a configuration problem to fix with your marketing team.
Load chat widgets and customer success tools lazily: These tools are almost never needed on the initial page load. Delay their initialisation until after the page becomes interactive:
window.addEventListener('load', () => {
// Load chat widget after everything else
loadChatWidget();
});Or better: initialise them on the first user interaction (scroll, mouse move, first click).
Audit your tag manager for blocking tags: Open your tag manager and review every tag's firing rule and loading type. Tags that fire on "page view" with synchronous execution block rendering. Change them to "DOM Ready" or "Window Loaded" firing rules.
Step 5: Preload Critical Resources
Render-blocking is partly about the browser discovering resources too late. Preloading tells the browser about critical resources earlier, so downloads start sooner even if the resource is discovered late in the HTML.
For your critical CSS (if you cannot inline it), preload it:
rel="preload" as="style" onload="this.rel='stylesheet'"
For your LCP image, add a preload link:
rel="preload" as="image" fetchpriority="high"
For your primary web font, preload the WOFF2 file:
rel="preload" as="font" type="font/woff2" crossorigin
Preloads should be for resources the browser cannot discover on its own early in the parse. Do not preload everything — it creates competition for bandwidth.
Validating the Impact
After applying these changes, compare:
- FCP: should drop as the browser can paint sooner
- LCP: often improves as the LCP image is no longer waiting behind blocking resources
- Total Blocking Time (TBT): a lab metric that correlates with INP; should drop as synchronous scripts are deferred
Use WebPageTest's waterfall view to visually confirm that the resources which were previously blocking are now loading in parallel with or after the initial render.
The common mistake after applying these fixes is seeing the lab score improve but missing a regression in a less-tested path — a logged-in page, a product detail page, a checkout step. Audit your highest-traffic pages, not just your homepage.
If your application uses a build system like Webpack, Vite, or Next.js, many of these optimisations can be automated at the build layer. Clixo can help you integrate critical CSS extraction and script loading strategy into your build pipeline if you want this handled systematically across your entire product.