All posts
·7 min read

How to Fix Render-Blocking Resources and Recover Your Lighthouse Score

Render-blocking CSS and JavaScript delay your First Contentful Paint and tank your Lighthouse performance score. Here is exactly how to find and fix them.

"Eliminate render-blocking resources" is one of the most common Lighthouse warnings — and also one of the highest-impact fixes you can make. It directly affects your First Contentful Paint (FCP) and Largest Contentful Paint (LCP), which are Core Web Vitals that Google uses for ranking.

This guide will show you exactly what the audit means, how to identify the specific resources causing the problem, and how to fix them.

What "render-blocking" means

When a browser loads a page, it builds the DOM from the HTML. Anytime it encounters a <script> or <link rel="stylesheet"> tag without a defer, async, or mediaattribute, it stops parsing the HTML and waits for that resource to download and execute. This is "render-blocking."

If your CSS is 400KB and your users are on mobile, they're staring at a blank screen for 2+ seconds while the browser downloads your stylesheet before it can paint anything.

Finding the culprits

In your Lighthouse report, expand the "Eliminate render-blocking resources" audit. It lists each blocking resource with its transfer size and estimated savings.

You can also open Chrome DevTools → Performance tab → record a page load. Look for the long purple bars labeled "Parse Stylesheet" or "Evaluate Script" that appear before the first green "Paint" event.

Fixing render-blocking JavaScript

Add defer to any script that doesn't need to run before the page renders:

<!-- Before -->
<script src="/app.js"></script>

<!-- After -->
<script src="/app.js" defer></script>

Use async only for scripts that are completely independent and order doesn't matter (analytics, chat widgets). defer preserves execution order; async does not.

For third-party scripts (Google Tag Manager, Intercom, Hotjar), load them after DOMContentLoaded or use a facade pattern:

document.addEventListener('DOMContentLoaded', () => {
  const script = document.createElement('script');
  script.src = 'https://www.googletagmanager.com/gtag/js?id=G-XXX';
  script.async = true;
  document.head.appendChild(script);
});

Fixing render-blocking CSS

CSS is trickier because you need it for the page to look correct. There are two approaches:

1. Inline critical CSS, defer the rest

"Critical CSS" is the CSS needed to render what's visible above the fold. Extract it and inline it in a <style> tag in your <head>, then load the full stylesheet non-blocking:

<head>
  <style>
    /* inlined critical CSS here */
    body { margin: 0; font-family: sans-serif; }
    .hero { display: flex; min-height: 100vh; }
  </style>
  <link rel="preload" href="/styles.css" as="style"
        onload="this.onload=null;this.rel='stylesheet'">
  <noscript><link rel="stylesheet" href="/styles.css"></noscript>
</head>

Tools like critical (npm) and critters (Webpack plugin) can automate this extraction.

2. Use media attributes for conditional stylesheets

If you have a stylesheet that only applies to print or specific screen sizes, tell the browser upfront so it won't block rendering:

<link rel="stylesheet" href="/print.css" media="print">
<link rel="stylesheet" href="/tablet.css" media="(min-width: 768px)">

The browser still downloads these but doesn't block rendering on them — it applies them once they're ready.

Framework-specific fixes

Next.js: Avoid putting <Script> tags with strategy="beforeInteractive" unless absolutely necessary. Use strategy="afterInteractive" or strategy="lazyOnload" instead.

WordPress: Use a plugin like WP Rocket or Perfmatters to defer non-critical scripts. Many themes load jQuery synchronously — deferring it can save 500ms+ on mobile.

What to expect after fixing

Eliminating render-blocking resources typically improves FCP by 0.5–2 seconds on mobile, which translates to a 5–15 point improvement in your Lighthouse Performance score. LCP improvements depend on what was blocking — if the LCP element was waiting on a blocking script, you may see improvements of 20+ points.

Real-user metrics (CrUX) take 4–6 weeks to reflect changes, since Chrome collects field data from actual users over a 28-day rolling window.

Scan your site and find these issues automatically

SEO Improvement detects render-blocking resources, gateway blocks, missing meta tags, and 20+ other issues — then gives you AI-generated steps to fix each one.

Scan your site free