How to Fix Render-Blocking Resources for Better Page Speed
Learn how to identify and eliminate render-blocking CSS and JavaScript that slow down your pages. Step-by-step guide with code examples.
Render-blocking resources are CSS and JavaScript files that prevent a page from displaying until they’re fully loaded. They’re one of the most common causes of poor Core Web Vitals and slow page loads.
Here’s how to identify and eliminate them.
What Are Render-Blocking Resources?
When a browser loads your page, it stops rendering whenever it encounters:
- CSS files in the
<head>(all CSS is render-blocking by default) - JavaScript files without
asyncordeferattributes
The browser waits for these files to download and process before showing anything to users.
The Rendering Process
- Browser starts parsing HTML
- Encounters
<link rel="stylesheet"> - Stops rendering to fetch and parse CSS
- Encounters
<script src="..."> - Stops rendering to fetch and execute JavaScript
- Continues parsing HTML
- Finally renders the page
Each blocking resource adds delay.
Identifying Render-Blocking Resources
PageSpeed Insights
Run your URL through PageSpeed Insights. Look for:
- “Eliminate render-blocking resources”
- Lists specific CSS/JS files causing delays
- Shows potential time savings
Chrome DevTools
- Open DevTools (F12)
- Go to Performance tab
- Record page load
- Look for long bars during “Parse HTML”
- These indicate blocking resources
Coverage Tool
- Open DevTools
- Press Ctrl+Shift+P
- Type “Coverage”
- Start recording
- Reload page
- See unused CSS/JS percentage
Fixing Render-Blocking CSS
1. Inline Critical CSS
Put essential above-the-fold styles directly in HTML:
<head>
<style>
/* Critical CSS for above-the-fold content */
body { margin: 0; font-family: system-ui; }
header { background: #333; color: white; padding: 1rem; }
.hero { padding: 2rem; text-align: center; }
h1 { font-size: 2rem; margin: 0 0 1rem; }
</style>
</head>
2. Defer Non-Critical CSS
Load remaining CSS without blocking:
<head>
<!-- Critical CSS inline -->
<style>/* Critical styles here */</style>
<!-- Non-critical CSS loaded asynchronously -->
<link rel="preload" href="/styles.css" as="style"
onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/styles.css"></noscript>
</head>
3. Use Media Queries
Load print/mobile CSS only when needed:
<!-- Only blocks on screens -->
<link rel="stylesheet" href="/main.css">
<!-- Doesn't block - only for print -->
<link rel="stylesheet" href="/print.css" media="print">
<!-- Doesn't block on desktop -->
<link rel="stylesheet" href="/mobile.css" media="(max-width: 768px)">
Extracting Critical CSS
Automatic Tools
Critical (npm package):
npm install critical --save-dev
const critical = require('critical');
critical.generate({
base: 'dist/',
src: 'index.html',
target: 'index-critical.html',
width: 1300,
height: 900,
inline: true
});
PurgeCSS:
npm install purgecss --save-dev
const { PurgeCSS } = require('purgecss');
const result = await new PurgeCSS().purge({
content: ['*.html'],
css: ['*.css']
});
Manual Critical CSS
For simple sites, manually identify critical styles:
- Load page with all CSS disabled
- Note what’s broken above the fold
- Find only those styles
- Inline them in the head
- Load rest of CSS asynchronously
Fixing Render-Blocking JavaScript
1. Use defer Attribute
defer loads scripts without blocking, executes after HTML parsing:
<!-- Blocking (bad) -->
<script src="/analytics.js"></script>
<!-- Non-blocking with defer (good) -->
<script defer src="/analytics.js"></script>
defer characteristics:
- Downloads in parallel with HTML parsing
- Executes after DOM is ready
- Maintains script order
- Best for scripts that need the DOM
2. Use async Attribute
async loads and executes independently:
<script async src="/analytics.js"></script>
async characteristics:
- Downloads in parallel
- Executes as soon as downloaded
- May execute before DOM ready
- No guaranteed order
- Best for independent scripts (analytics, ads)
3. Move Scripts to End of Body
For legacy browser support:
<body>
<!-- All content -->
<!-- Scripts at end -->
<script src="/app.js"></script>
</body>
When to Use Each
| Script Type | Use | Example |
|---|---|---|
| Framework/App | defer | React, Vue, main app |
| Analytics | async | Google Analytics |
| Ads | async | Google Ads |
| Critical features | inline | Core functionality |
| Social widgets | async | Share buttons |
Inline Small Scripts
For tiny scripts (< 1KB), inline them:
<!-- Instead of loading a separate file -->
<script src="/small-script.js"></script>
<!-- Inline it -->
<script>
// Small script contents here
document.querySelector('.menu-toggle').addEventListener('click', () => {
document.querySelector('.menu').classList.toggle('open');
});
</script>
Preload Critical Resources
Use preload for resources needed immediately:
<head>
<!-- Preload critical font -->
<link rel="preload" href="/fonts/main.woff2" as="font"
type="font/woff2" crossorigin>
<!-- Preload hero image -->
<link rel="preload" href="/hero.webp" as="image">
<!-- Preload critical JavaScript -->
<link rel="preload" href="/critical.js" as="script">
</head>
Code Splitting
Don’t load everything upfront. Split code by route or component:
Dynamic Imports
// Instead of importing everything
import { HeavyComponent } from './heavy';
// Load on demand
const HeavyComponent = await import('./heavy');
Route-Based Splitting
// React with lazy loading
const Dashboard = React.lazy(() => import('./Dashboard'));
const Settings = React.lazy(() => import('./Settings'));
Framework Support
Most modern frameworks support automatic code splitting:
- Next.js: Automatic per-page
- Nuxt: Automatic per-route
- Astro: Automatic (ships zero JS by default)
- Vite: Automatic with dynamic imports
Optimization Example
Before (render-blocking):
<head>
<link rel="stylesheet" href="/normalize.css">
<link rel="stylesheet" href="/bootstrap.css">
<link rel="stylesheet" href="/custom.css">
<script src="/jquery.js"></script>
<script src="/plugins.js"></script>
<script src="/main.js"></script>
</head>
After (optimized):
<head>
<!-- Critical CSS inline -->
<style>
body{margin:0;font-family:system-ui}
/* More critical styles */
</style>
<!-- Preload critical resources -->
<link rel="preload" href="/main.js" as="script">
<!-- Defer non-critical CSS -->
<link rel="preload" href="/styles.css" as="style"
onload="this.onload=null;this.rel='stylesheet'">
</head>
<body>
<!-- Content -->
<!-- Scripts with defer at end -->
<script defer src="/main.js"></script>
</body>
Measuring Improvements
Core Web Vitals Impact
Eliminating render-blocking resources improves:
- LCP: Faster first paint means faster largest paint
- FCP: First Contentful Paint happens sooner
- TTI: Time to Interactive improves
Metrics to Track
| Metric | Good | Needs Work | Poor |
|---|---|---|---|
| FCP | < 1.8s | 1.8-3s | > 3s |
| LCP | < 2.5s | 2.5-4s | > 4s |
| Total Blocking Time | < 200ms | 200-600ms | > 600ms |
Testing Tools
- Lighthouse: Detailed performance audit
- WebPageTest: Waterfall analysis
- Chrome DevTools Performance: Timeline view
- PageSpeed Insights: Field and lab data
Common Mistakes
1. Using async for App Scripts
<!-- Wrong - scripts may execute out of order -->
<script async src="/vendor.js"></script>
<script async src="/app.js"></script>
<!-- Right - maintains order -->
<script defer src="/vendor.js"></script>
<script defer src="/app.js"></script>
2. Forgetting noscript Fallback
<!-- Missing fallback for users without JS -->
<link rel="preload" href="/styles.css" as="style"
onload="this.onload=null;this.rel='stylesheet'">
<!-- With fallback -->
<link rel="preload" href="/styles.css" as="style"
onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/styles.css"></noscript>
3. Inlining Too Much CSS
Critical CSS should be small (< 14KB). Don’t inline everything:
<!-- Bad - too much inline CSS -->
<style>/* 50KB of CSS */</style>
<!-- Good - only above-the-fold critical CSS -->
<style>/* 5KB of critical CSS */</style>
<link rel="preload" href="/full-styles.css" as="style" ...>
Checklist
- Run PageSpeed Insights to identify blocking resources
- Extract and inline critical CSS
- Defer non-critical CSS loading
- Add
deferto non-critical scripts - Add
asyncto independent scripts (analytics) - Move scripts to end of body if needed
- Preload critical resources
- Implement code splitting
- Test and measure improvements
- Monitor Core Web Vitals
Related Resources:
Tags:
Share this article:
Ready to improve your SEO?
Get a free SEO audit and see exactly what needs fixing on your site
Start Free Audit