Defer non-critical CSS

Demián Renzulli
Demián Renzulli

CSS files are render-blocking resources: they must be loaded and processed before the browser renders the page. Web pages that contain unnecessarily large style sheets take longer to render.

Learn how to defer non-critical CSS to optimize the Critical Rendering Path and improve First Contentful Paint (FCP).

Suboptimal CSS loading

The following example contains an accordion with three hidden paragraphs of text, each of which is styled with a different class:

This page requests a CSS file with eight classes, but not all of them are necessary to render the "visible" content.

The goal of this guide is to optimize this page so only the critical styles are loaded synchronously, while the rest (including the paragraph styles), are loaded in a non-blocking way.

Measure

Run Lighthouse in DevTools to review impactful metrics.

  1. Open the demo in Chrome.
  2. Open Chrome DevTools.
  3. Select the Performance panel.
  4. From inside the panel, reload the page.

The report shows the First Contentful Paint metric with a value of "1s", and the opportunity Eliminate render-blocking resources, pointing to the style.css file:

Lighthouse
    report for unoptimized page, showing FCP of '1s' and 'Eliminate blocking
    resources' under 'Opportunities'
The Lighthouse report suggests simplifying your style sheet to make your page load faster.

In the resulting trace, the FCP marker is placed immediately after the CSS finishes loading:

DevTools performance trace for unoptimized page, showing FCP starting after CSS loads.
On the unoptimized demo page, FCP can't happen until the CSS finishes loading.

This means the browser needs to wait for all CSS to load and get processed before painting a single pixel on the screen.

Optimize

To optimize this page, use the Coverage Tool to determine which classes are considered critical.

  1. Open the DevTools Command Menu by pressing Control+Shift+P or Command+Shift+P (Mac).
  2. Type "Coverage" and select Show Coverage.
  3. Click Reload to reload the page and start capturing the coverage.
Coverage for CSS file, showing 55.9% unused bytes.
The coverage report shows how much of your CSS is actually used in the initial page load.

Double-click the report to see details:

  • Classes marked in green are critical. The browser needs them to render the visible content, including the title, subtitle, and accordion buttons.
  • Classes marked in red are non-critical, only affecting content that's not immediately visible, like the hidden paragraphs.

With this information, optimize your CSS so the browser can start processing critical styles immediately after the page loads and defer non-critical CSS for later:

  1. Extract the class definitions marked with green in the coverage report, and put those classes in a <style> block at the head of the page:

    <style type="text/css">
    .accordion-btn {background-color: #ADD8E6;color: #444;cursor: pointer;padding: 18px;width: 100%;border: none;text-align: left;outline: none;font-size: 15px;transition: 0.4s;}.container {padding: 0 18px;display: none;background-color: white;overflow: hidden;}h1 {word-spacing: 5px;color: blue;font-weight: bold;text-align: center;}
    </style>
    
  2. Load the rest of the classes asynchronously by applying the following pattern:

    <link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
    <noscript><link rel="stylesheet" href="styles.css"></noscript>
    

This isn't the standard way of loading CSS. Here's how it works:

  • link rel="preload" as="style" requests the style sheet asynchronously. Learn more about preload in the Preload critical assets guide.
  • The onload attribute in the link lets the browser process the CSS when the style sheet finishes loading.
  • "nulling" the onload handler after it's used helps some browsers avoid re-calling the handler when they switch the rel attribute.
  • The reference to the style sheet inside the noscript element provides a fallback for browsers that don't execute JavaScript.

In production

In production, we recommend using CSS-deferring functions, such as loadCSS, that encapsulate this behavior and work well across browsers. These functions support a Content Security Policy, which might not allow inline onload JavaScript.

You can also place the CSS link at the bottom of the page, so content may render without waiting for the stylesheet to load in the browser. However, the browser still prioritizes the stylesheet, so it can still block critical content in the browser.

The resulting page looks exactly like the previous version, even when most styles load asynchronously.

Monitor

Use DevTools to run another Performance trace on the optimized page.

The FCP marker appears before the page requests the CSS, which means the browser doesn't need to wait for the CSS to load before rendering the page:

DevTools
    performance trace for optimized page, showing FCP starting before CSS
    loads.
On the optimized page, FCP can start before the style sheet loads.

As a final step, run Lighthouse on the optimized page.

In the report, you'll see that the FCP page has been reduced by 0.2s (a 20% improvement!):

Lighthouse report, showing an FCP value of '0.8s'.

The Eliminate render-blocking resources suggestion no longer appears under Opportunities, and is instead in the Passed Audits section:

A depiction
    of Lighthouse report, showing 'Eliminate blocking resources' on the 'Passed
    Audits' section.
The page now passes the blocking resources audit.

Next steps and references

For more complex production environments, the extract critical CSS guide covers some of the most popular tools to extract critical CSS and includes a codelab to see how they work in practice.