Skip to content

Render Blocking Resources: How to Fix CSS and JS Bottlenecks

Last Updated: August 10, 2026

What Render Blocking Means

When a browser encounters a render-blocking resource in your HTML, it stops parsing and rendering the page until that resource is downloaded and processed. The browser cannot paint any pixels to the screen while blocked, even if the rest of the HTML is ready. This creates a direct, measurable delay between the browser receiving your HTML and displaying visible content to the user.

CSS is inherently render-blocking. The browser cannot know which styles apply to which elements without first downloading and parsing every stylesheet referenced in the HTML. A single external stylesheet blocks rendering until the entire file transfers and the browser's CSS engine processes it. This is why a page with 10 external CSS files in the head section takes significantly longer to display visible content than the same page with one optimized CSS file.

JavaScript is conditionally render-blocking. When the browser encounters a script tag without the defer or async attribute, it pauses HTML parsing, downloads the script, executes it, and then resumes parsing. This behavior exists because JavaScript can modify the DOM, so the browser must execute scripts in order to know what content to render.

CSS: The Biggest Render Blocker

External stylesheets in the head section are the most common cause of slow First Contentful Paint. Every CSS file referenced with a link tag in the head creates a blocking request. The browser must download, parse, and apply all linked stylesheets before rendering any content.

Identify your render-blocking CSS by running Lighthouse. The "Eliminate render-blocking resources" audit lists every CSS file that delays rendering, showing the potential savings in milliseconds. A typical audit reveals 3-8 blocking resources with combined delays of 500ms-2 seconds.

The solution is critical CSS: extracting the CSS needed to render above-the-fold content and inlining it in the head section, then loading the remaining CSS asynchronously. This technique ensures that the visible portion of the page renders immediately while the full stylesheet loads in the background:

<!-- Critical CSS inlined -->
<style>
  .header { padding: 20px; background: #fff; }
  .hero { max-width: 1200px; margin: 0 auto; }
  .hero h1 { font-size: 2.5rem; line-height: 1.2; }
</style>

<!-- Full CSS loaded asynchronously -->
<link rel="preload" href="/styles/full.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/styles/full.css"></noscript>

Generate critical CSS with tools like critical or penthouse, which analyze your page and extract the styles applied to above-the-fold elements. Our page speed checker identifies which CSS files block rendering and estimates the time savings from inlining critical CSS.

JavaScript: defer, async, and Normal

Three script loading strategies exist, and choosing the wrong one is one of the most common performance mistakes:

Normal (no attribute): Blocks HTML parsing. The browser stops building the DOM, downloads the script, executes it, then continues parsing. Use this only for scripts that must execute before any content renders, like scripts that initialize critical state or polyfills that modify browser behavior.

<!-- Blocks parsing until script downloads and executes -->
<script src="analytics.js"></script>
<!-- HTML parsing resumes here -->

Defer: Downloads the script in parallel with HTML parsing but defers execution until parsing completes. Scripts execute in document order. This is the safest non-blocking option for most scripts:

<!-- Downloads in parallel, executes after HTML parsing -->
<script defer src="analytics.js"></script>
<!-- HTML continues parsing immediately -->

Async: Downloads in parallel with HTML parsing but executes immediately upon download completion, pausing HTML parsing during execution. Scripts execute in the order they download, which may not match document order. Use async for independent scripts that do not depend on other scripts or DOM state, like ad tags or third-party analytics:

<!-- Downloads in parallel, executes immediately when ready -->
<script async src="third-party-ad.js"></script>

Critical CSS Strategy

Implementing critical CSS requires identifying the minimum styles needed to render above-the-fold content. Extract these styles from your CSS files and place them in a style tag in the head section of your HTML. Load the full stylesheet asynchronously so the complete styles apply after the initial render.

A practical implementation for a WordPress site uses a build script to extract critical CSS automatically. Install the critical npm package and configure it with viewport dimensions matching your most common above-the-fold layout:

const critical = require('critical');

critical.generate({
  base: 'dist/',
  src: 'index.html',
  css: ['dist/styles/main.css'],
  dimensions: [{ width: 375, height: 667 }, { width: 1440, height: 900 }],
  inline: true,
  extract: true,
});

The tool generates a version of your HTML with critical CSS inlined and the full stylesheet reference deferred. Deploy the critical CSS version to production. For dynamic pages (CMS-generated content), extract critical CSS at build time for each template rather than at runtime to avoid adding processing delay to each request.

Font Loading Strategy

Web fonts loaded with the standard link tag are render-blocking. The browser pauses text rendering until the font file downloads, causing invisible text (FOIT) or a flash of unstyled text (FOUT). This is especially problematic because fonts are often large files (20-100KB each) that must download before any text renders.

Use font-display: optional in your CSS @font-face declarations to eliminate font-related render blocking. This strategy shows the web font if it loads within the first 100 milliseconds and falls back to the system font immediately if it does not. There is no flash, no invisible text, and no layout shift:

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-variable.woff2') format('woff2');
  font-display: optional;
  unicode-range: U+0000-00FF;
}

Alternatively, use font-display: swap to show system fonts immediately and swap to web fonts once they load. This produces a brief flash of different fonts but eliminates the invisible text period. Combine with preload hints to start font downloads earlier:

<link rel="preload" href="/fonts/inter-variable.woff2" as="font" type="font/woff2" crossorigin>

Auditing with Lighthouse

Lighthouse provides specific, actionable data about your render-blocking resources. Run it from Chrome DevTools (F12 > Lighthouse tab) or from the command line:

npx lighthouse https://example.com --view
# Opens report in browser with detailed performance metrics

The "Eliminate render-blocking resources" audit lists every blocking CSS and JavaScript file, showing the time each file delays rendering. The "Reduce unused CSS" audit identifies CSS rules that are never applied to the current page. Together, these audits give you a prioritized list of optimizations with measurable impact estimates.

Track your Lighthouse scores over time using our page speed checker. The tool monitors your First Contentful Paint, Largest Contentful Paint, and Total Blocking Time scores, alerting you when render-blocking resources are added or when existing resources grow in size beyond acceptable thresholds.