Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.

Load CSS without blocking render

Non-critical CSS is loaded asynchronously to avoid blocking DOM rendering.

Utilities
Quick take
Typical fix time 20 min
  • Use `<link rel="preload" as="style">` with onload handler
  • Inline critical CSS, defer non-critical styles
  • Use media queries to split CSS by viewport/feature
  • Modern: Next.js/Vite handle this automatically
Why it matters: Render-blocking CSS delays First Contentful Paint—users see a blank screen while waiting for stylesheets to download and parse.

Rule Details

CSS files should be loaded in a non-blocking manner to prevent them from delaying DOM parsing and initial page rendering.

Code Example

<!-- ❌ Blocking CSS - delays DOM parsing -->
<head>
    <link rel="stylesheet" href="styles.css">
    <link rel="stylesheet" href="large-library.css">
</head>

Why It Matters

Render-blocking CSS delays First Contentful Paint—users see a blank screen while waiting for stylesheets to download and parse.

Non-Blocking Solutions

1. Preload with Media Attribute

<head>
    <!-- Critical CSS inline -->
    <style>
        /* Critical above-the-fold styles */
        .header { background: #000; color: #fff; }
        .hero { min-height: 50vh; }
    </style>
 
    <!-- Non-blocking CSS loading -->
    <link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
    <noscript><link rel="stylesheet" href="styles.css"></noscript>
 
    <!-- Load non-critical CSS with media attribute -->
    <link rel="stylesheet" href="print.css" media="print">
    <link rel="stylesheet" href="mobile.css" media="screen and (max-width: 768px)">
</head>

2. LoadCSS Library Implementation

<head>
    <!-- Critical CSS inline -->
    <style>
        /* Critical styles here */
    </style>
 
    <!-- LoadCSS script -->
    <script>
        /*! loadCSS. [c]2017 Filament Group, Inc. MIT License */
        !function(a){"use strict";var b=function(b,c,d){function e(a){return h.body?a():void setTimeout(function(){e(a)})}function f(){i.addEventListener&&i.removeEventListener("load",f),i.media=d||"all"}var g,h=a.document,i=h.createElement("link");if(c)g=c;else{var j=(h.body||h.getElementsByTagName("head")[0]).childNodes;g=j[j.length-1]}var k=h.styleSheets;i.rel="stylesheet",i.href=b,i.media="only x",e(function(){g.parentNode.insertBefore(i,c?g:g.nextSibling)});var l=function(a){for(var b=i.href,c=k.length;c--;)if(k[c].href===b)return a();setTimeout(function(){l(a)})};return i.addEventListener&&i.addEventListener("load",f),i.onloadcssdefined=l,l(f),i};"undefined"!=typeof exports?exports.loadCSS=b:a.loadCSS=b}("undefined"!=typeof global?global:this);
    </script>
 
    <!-- Load non-critical CSS -->
    <script>
        loadCSS('styles.css');
        loadCSS('components.css');
        loadCSS('vendor.css');
    </script>
 
    <!-- Fallback for no-JS -->
    <noscript>
        <link rel="stylesheet" href="styles.css">
        <link rel="stylesheet" href="components.css">
        <link rel="stylesheet" href="vendor.css">
    </noscript>
</head>

Framework Examples

Next.js (Automatic Optimization)

// Next.js automatically optimizes CSS loading
import './globals.css'  // Critical CSS
import './components.css'  // Non-critical CSS
 
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

React with Critical CSS

// Critical CSS component
import { useEffect } from 'react'
 
function App() {
  useEffect(() => {
    // Load non-critical CSS after component mounts
    const loadCSS = (href) => {
      const link = document.createElement('link')
      link.rel = 'stylesheet'
      link.href = href
      document.head.appendChild(link)
    }
 
    loadCSS('/styles/non-critical.css')
    loadCSS('/styles/components.css')
  }, [])
 
  return (
    <div className="app">
      <style jsx>{`
        /* Critical inline styles */
        .app {
          min-height: 100vh;
          display: flex;
          flex-direction: column;
        }
      `}</style>
      {/* Your app content */}
    </div>
  )
}

Vue.js with Async CSS

<template>
  <div class="app">
    <!-- Your app content -->
  </div>
</template>
 
<script>
export default {
  mounted() {
    // Load non-critical CSS after mount
    this.loadCSS('/styles/components.css')
    this.loadCSS('/styles/animations.css')
  },
  methods: {
    loadCSS(href) {
      const link = document.createElement('link')
      link.rel = 'preload'
      link.as = 'style'
      link.href = href
      link.onload = function() {
        this.onload = null
        this.rel = 'stylesheet'
      }
      document.head.appendChild(link)
    }
  }
}
</script>
 
<style scoped>
/* Critical component styles */
.app {
  min-height: 100vh;
}
</style>

Modern CSS Loading Strategies

1. Critical CSS Inlining

<head>
    <style>
        /* Inline critical CSS - above-the-fold content */
        body { margin: 0; font-family: Arial, sans-serif; }
        .header { background: #000; color: #fff; height: 60px; }
        .hero { min-height: 50vh; background: #f0f0f0; }
    </style>
</head>

2. Resource Hints

<head>
    <!-- Preconnect to font/CDN domains -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://cdn.jsdelivr.net">
 
    <!-- Preload critical CSS -->
    <link rel="preload" href="critical.css" as="style">
 
    <!-- Prefetch non-critical CSS -->
    <link rel="prefetch" href="animations.css">
</head>

3. Service Worker CSS Caching

// sw.js
self.addEventListener('fetch', (event) => {
  if (event.request.destination === 'style') {
    event.respondWith(
      caches.match(event.request)
        .then(response => response || fetch(event.request))
    )
  }
})

Performance Benefits

  • Faster First Paint: DOM parsing isn't blocked by CSS downloads
  • Better Core Web Vitals: Improved LCP (Largest Contentful Paint)
  • Progressive Enhancement: Page is functional while styles load
  • Perceived Performance: Users see content sooner

Build Tool Integration

Webpack

// webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
 
module.exports = {
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
      chunkFilename: '[id].[contenthash].css',
    })
  ],
  optimization: {
    splitChunks: {
      cacheGroups: {
        styles: {
          name: 'styles',
          test: /\.css$/,
          chunks: 'all',
          enforce: true,
        },
      },
    },
  },
}

Vite

// vite.config.js
import { defineConfig } from 'vite'
 
export default defineConfig({
  build: {
    cssCodeSplit: true,
    rollupOptions: {
      output: {
        manualChunks: {
          critical: ['./src/styles/critical.css'],
          components: ['./src/styles/components.css']
        }
      }
    }
  }
})

Tools & Libraries

Support Notes

  • Non-blocking CSS strategies can behave differently depending on preload, stylesheet priority, and browser loading heuristics, so verify the final page output in target browsers.
  • Document the fallback when a loading optimization depends on browser support or a framework-specific resource pipeline.

Verification

Automated Checks

  • Lighthouse: Check for render-blocking resources
  • Chrome DevTools: Network tab to see resource loading order

Manual Checks

  • PageSpeed Insights: Analyze CSS loading performance
  • WebPageTest: Visualize CSS loading timeline

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Analyze this CSS loading implementation to ensure stylesheets are loaded without blocking the DOM parsing and rendering process.

Fix

Auto-fix issues

Implement non-blocking CSS loading using the preload technique with media attributes and loadCSS polyfill for better performance.

Explain

Learn more

Explain how non-blocking CSS loading improves page performance by preventing render-blocking resources from delaying the initial paint.

Review

Code review

Review stylesheets, component styles, and responsive states related to Load CSS without blocking render. Flag exact selectors, declarations, or breakpoints that violate the rule in the rendered UI.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

Chrome DevTools
developer.chrome.comTool

Rules that often go hand-in-hand with this one.

Order CSS files correctly

All CSS files are loaded before JavaScript files to prevent render blocking.

CSS
Inline critical CSS for faster rendering

Critical CSS (above-the-fold content) is inlined in the head for faster initial render.

CSS
Load scripts with defer, async, or type=module

Prevent JavaScript from blocking HTML parsing by using defer, async, or type=module attributes on script tags so the browser can continue building the DOM while scripts download.

HTML
Optimize third-party script loading

Load third-party scripts asynchronously to prevent blocking the main thread and improve page performance.

Performance

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385