Technical SEO 10 min read

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.

By Rankture Team
How to Fix Render-Blocking Resources for Better Page Speed

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:

The browser waits for these files to download and process before showing anything to users.

The Rendering Process

  1. Browser starts parsing HTML
  2. Encounters <link rel="stylesheet">
  3. Stops rendering to fetch and parse CSS
  4. Encounters <script src="...">
  5. Stops rendering to fetch and execute JavaScript
  6. Continues parsing HTML
  7. Finally renders the page

Each blocking resource adds delay.

Identifying Render-Blocking Resources

PageSpeed Insights

Run your URL through PageSpeed Insights. Look for:

Chrome DevTools

  1. Open DevTools (F12)
  2. Go to Performance tab
  3. Record page load
  4. Look for long bars during “Parse HTML”
  5. These indicate blocking resources

Coverage Tool

  1. Open DevTools
  2. Press Ctrl+Shift+P
  3. Type “Coverage”
  4. Start recording
  5. Reload page
  6. 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:

  1. Load page with all CSS disabled
  2. Note what’s broken above the fold
  3. Find only those styles
  4. Inline them in the head
  5. 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:

2. Use async Attribute

async loads and executes independently:

<script async src="/analytics.js"></script>

async characteristics:

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 TypeUseExample
Framework/AppdeferReact, Vue, main app
AnalyticsasyncGoogle Analytics
AdsasyncGoogle Ads
Critical featuresinlineCore functionality
Social widgetsasyncShare 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:

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:

Metrics to Track

MetricGoodNeeds WorkPoor
FCP< 1.8s1.8-3s> 3s
LCP< 2.5s2.5-4s> 4s
Total Blocking Time< 200ms200-600ms> 600ms

Testing Tools

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


Related Resources:

Tags:

render blocking page speed core web vitals css javascript

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