# Eliminate render-blocking resources: what it means and how to fix it

**Audit ID:** `render-blocking-resources` · **Category:** Performance

The browser can't render anything until it finishes downloading and parsing every render-blocking CSS and synchronous JS in the `<head>`. Each blocking resource adds latency to FCP and LCP.

## TL;DR

- **What:** CSS and synchronous `<script>` tags in `<head>` that block the browser from rendering.
- **Target:** Zero render-blocking resources on the critical path. Inline critical CSS, defer the rest.
- **Top three fixes:** inline critical CSS, defer non-critical stylesheets, add `defer`/`async` to scripts.

## What the audit checks

Lighthouse identifies every CSS link tag and synchronous `<script>` in `<head>` that delays the first paint. It estimates how much time you'd save by deferring each one.

## Why it matters

The browser parses HTML top-down. When it hits:

```html
<link rel="stylesheet" href="big-app.css" />
```

…it stops, downloads `big-app.css`, parses it, then continues. If your CSS is 200 KB on a slow network, that's a 1-2 second pause before any pixels paint. Same for synchronous JS in `<head>`.

## Three fix tactics

### 1. Inline critical CSS

"Critical CSS" = the styles needed to render the above-the-fold portion of the page. Inline it directly in `<head>`:

```html
<head>
  <style>
    /* critical: header, hero, primary nav */
    body { margin: 0; font-family: system-ui; }
    header { padding: 16px; background: #fafaf9; }
    h1 { font-size: 2rem; }
  </style>
  <link rel="preload" as="style" href="/full.css" onload="this.rel='stylesheet'" />
</head>
```

For larger sites, tools like [Critical](https://github.com/addyosmani/critical), [Penthouse](https://github.com/pocketjoso/penthouse), or framework features (Next.js does this automatically in some configurations) extract critical CSS at build time.

### 2. Defer non-critical CSS

```html
<!-- WRONG: blocks rendering -->
<link rel="stylesheet" href="/non-critical.css" />

<!-- RIGHT: load asynchronously, apply after parse -->
<link rel="preload" as="style" href="/non-critical.css" onload="this.rel='stylesheet'" />
<noscript><link rel="stylesheet" href="/non-critical.css" /></noscript>
```

The `onload` swap converts the preload into a stylesheet once it's loaded, without blocking. The `<noscript>` fallback ensures JS-disabled users still get styles.

### 3. Add `defer` or `async` to scripts

```html
<!-- WRONG: blocks the parser -->
<script src="app.js"></script>

<!-- defer: runs after document parse, preserves order -->
<script src="app.js" defer></script>

<!-- async: runs whenever it's downloaded (no order guarantee) -->
<script src="analytics.js" async></script>
```

Rule of thumb:
- **`defer`** for scripts that need DOM access and depend on each other (your app code)
- **`async`** for independent scripts that don't depend on order (analytics, error tracking)
- **Move below `</body>`** if you can't use either, old-school but still works

### 4. Load fonts strategically

Web fonts are a common silent render-blocker. Best practice:

```html
<head>
  <!-- preconnect to the font origin so DNS/TLS is warm -->
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />

  <!-- preload the most critical font file (typically the body font) -->
  <link rel="preload" as="font" type="font/woff2"
        href="https://fonts.gstatic.com/.../inter.woff2" crossorigin />

  <!-- load the CSS asynchronously -->
  <link rel="preload" as="style"
        href="https://fonts.googleapis.com/css2?family=Inter"
        onload="this.rel='stylesheet'" />
</head>
```

Or self-host fonts and use `font-display: swap` so text renders with a fallback font immediately.

## Common mistakes

- **Inlining the entire stylesheet.** That gives you all of it on every page even if 90% is unused. Inline only critical (~5–10 KB).
- **Defer-loading critical CSS.** If your above-the-fold renders unstyled (FOUC), critical CSS isn't inlined, fix that first.
- **Forgetting `<noscript>` fallback** for the preload+onload pattern. JS-disabled users will see unstyled content otherwise.

## Verification

1. Re-run Lighthouse, `render-blocking-resources` audit should pass or show much smaller estimated savings.
2. Check `first-contentful-paint` and `largest-contentful-paint` audits, both should improve.
3. DevTools → Network → throttle to Slow 3G → reload. The page should paint within ~1-2 seconds even on slow connections.

## Related audits

- [Largest Contentful Paint (LCP)](/audits/largest-contentful-paint), downstream of render-blocking fixes
- [Reduce unused JavaScript](/audits/reduce-unused-javascript), trim what does load
- [Total Blocking Time (TBT)](/audits/total-blocking-time), main-thread blocking

---

Audit your URL at https://lighthouse-md.com.
