"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.