Critical CSS and render-blocking resources explained

CSS and JavaScript can stop a page from showing. What render-blocking means, how critical CSS, defer and async fix it, and the trade-offs of each.

IntermediateHow-to6 min readUpdated October 29, 2026

In this guide
  1. What render-blocking means
  2. What Lighthouse counts
  3. Fixing JavaScript: defer, async and modules
  4. Fixing CSS: load less, and load it differently
    1. 1. Ship less CSS
    2. 2. Inline the critical CSS
    3. 3. Split CSS by page or section
  5. Trade-offs
  6. Fonts
  7. A sensible order of work
  8. How to check
  9. Check your own page
  10. Sources

What render-blocking means

CSS is render-blocking by design. Rules later in a stylesheet can override earlier ones, so the browser waits until it has received and processed all the CSS before it draws anything, instead of drawing unstyled content and then redrawing. Every stylesheet linked in the head therefore holds back the first paint until it has been downloaded and read.

Scripts work differently, and the difference matters. A script with no defer, async or type="module" blocks the HTML parser: the browser must download and run it before it reads the rest of the page. Strictly, it blocks parsing rather than rendering. In practice a script in the head still delays the first paint, because the browser cannot draw content it has not read yet.

When there are many blocking files, or they are large or slow to arrive, the visitor looks at a blank page. They hurt First Contentful Paint and, when the main element is text, LCP. Lighthouse 13 lists them under Render-blocking requests; older reports call the same thing "Eliminate render-blocking resources".

What Lighthouse counts

The insight lists requests that the browser marked as render-blocking and that finished before the first paint. A stylesheet in the head qualifies, and so does a synchronous script in the head. For scripts placed later in the body, only the ones with a high priority are listed. Each row shows the request and how long it took, and Lighthouse estimates the saving for requests that took 50 milliseconds or more. Its estimate treats the problem as one of the first paint first, and as an LCP problem when the LCP element is text rather than an image.

Fixing JavaScript: defer, async and modules

Add one of these to scripts that do not have to run before the page is drawn:

<script src="/js/app.js" defer></script>
<script src="/js/analytics.js" async></script>
<script src="/js/app.mjs" type="module"></script>

A script that must run first, such as one that decides which version of the page to show, can stay as it is. Keep those very small.

Fixing CSS: load less, and load it differently

CSS is harder, because the page really does need it. There are three ways to reduce the blocking.

1. Ship less CSS

Remove styles a page does not use. (The CSS performance guide covers the costs beyond loading.) Many themes and frameworks send one very large stylesheet to every page, and smaller files arrive sooner. The Coverage tool in Chrome DevTools shows how much of each file a page actually uses, and many performance plugins offer a remove unused CSS option.

2. Inline the critical CSS

Critical CSS is the small set of rules needed to draw the first screen. It goes inline in the head, so the browser has what it needs with no extra download. The full stylesheet is then loaded without blocking:

<style>
  /* only what the first screen needs */
  body { margin: 0; font-family: system-ui, sans-serif; }
  .hero { min-height: 60vh; display: grid; place-items: center; }
</style>
<link rel="preload" href="/css/site.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/css/site.css"></noscript>

The preload line starts downloading the full stylesheet without blocking the page, and the onload attribute turns it into a real stylesheet when it has arrived. The noscript line covers visitors without JavaScript. An inline onload handler is blocked by a strict Content-Security-Policy; if your site has one, let your build tool or performance plugin generate the equivalent code with a nonce or hash.

Why not load the CSS without blocking and stop there? The page would first be drawn with no styles and then redraw when the CSS arrives. That flash of unstyled content looks broken and can cause layout shifts. Critical CSS is what makes non-blocking CSS safe. On its own it does nothing for speed. It pays off together with a non-blocking load of the rest.

3. Split CSS by page or section

Load the styles for the checkout only on the checkout. With HTTP/2 and HTTP/3, several small files are fine, so one giant bundle is not a requirement. Avoid @import inside CSS files: each imported file is only discovered after the one that imports it has downloaded, which builds a chain.

Trade-offs

Fonts

Web fonts hold text back in a similar way. With the default setting, the browser may show invisible text until the font arrives. Use font-display: swap so text shows in a fallback font straight away, or optional to use the web font only if it is already there, and preload the one font the top of the page needs.

A sensible order of work

On a typical site, whether it is WordPress with a theme and a handful of plugins or a hand-built one, this order gets the most for the least effort:

  1. Add `defer` to scripts that nothing needs before the first paint. It is one word per script and rarely breaks anything. Test the menu, forms and sliders afterwards.
  2. Remove styles and scripts the page does not use. A plugin that loads its stylesheet on every page for a feature that exists on one page is a common find.
  3. Turn on or build critical CSS. Use your performance plugin's option, or a build step, and look at the page with a slow connection to confirm that nothing jumps.
  4. Handle fonts. Set font-display and preload the one font the top of the page needs.
  5. Test again and compare. The list of render-blocking requests should be shorter, and First Contentful Paint should be earlier.

Stop when the list is empty or when the remaining files are tiny. A single small stylesheet that arrives in one round trip is not worth the complexity of splitting it.

How to check

PageSpeed Insights shows the render-blocking requests with their file names and durations. After a change, run the test again and check two things: the list is shorter, and the page still looks right while it loads. Watch the first second of a load on a slow connection, and check CLS, because a mismatch shows up as a jump.

Check your own page

Run the free audit and look for render-blocking requests. If the report lists a large stylesheet and a few scripts, start with the scripts, because adding defer is a one-word change, and then move on to the CSS.

Sources

Start here

Free website audit

Live Google data for any public page: mobile and desktop.

Mobile + desktop · Lighthouse lab test · Real-user Chrome data when Google has it

Rather have this done for you? Speed optimization · Book a call

Related guides

All guides →