What Is Render-Blocking and How to Eliminate It
Render-blocking CSS and synchronous JavaScript delay first paint. What it is, how to find it in Lighthouse, and how to eliminate it to improve FCP and LCP.
A visitor stares at a blank white screen. The HTML has arrived, the server responded quickly, and yet nothing is on screen — because the browser is still waiting for a stylesheet and a script in the page's <head> to finish before it will paint a single pixel. Those are render-blocking resources, and they are one of the most common reasons a page that should be fast still feels slow to appear. In short: render-blocking resources are CSS and synchronous JavaScript that the browser must download and process before it can render any content, delaying First Contentful Paint and often Largest Contentful Paint; you eliminate them by deferring or async-loading scripts, inlining critical CSS while lazy-loading the rest, and removing what is unused. This guide explains exactly how render-blocking works, how to find it, and how to clear it.
It is the deep-dive behind the "optimise the critical rendering path" step in how to make your website load faster.
What render-blocking means
To display a page, the browser builds two structures: the DOM (from the HTML) and the CSSOM (from the CSS). It needs both before it can render, because it has to know not just what the content is but how it should look. A render-blocking resource is anything that stops the browser from completing this process and painting — and the two usual culprits are CSS and synchronous JavaScript loaded in the <head>.
The result is a delay to First Contentful Paint (FCP) — the moment the first text or image appears — and frequently to Largest Contentful Paint (LCP) as well, since the largest element cannot render while the browser is blocked. On a fast connection the delay may be small; on a slow connection, where each blocking file takes a noticeable time to fetch, the page can sit blank for seconds. That blank period is precisely what eliminating render-blocking resources targets.
Why CSS blocks rendering
CSS is render-blocking by design, and for a good reason. If the browser painted the page before the stylesheets had loaded, the user would briefly see raw, unstyled HTML — a jarring flash of unstyled content (FOUC) — that then snaps into its styled layout once the CSS applies. To avoid this, the browser waits for the CSS referenced in the <head> to download and parse before it paints anything.
This means the problem with CSS is not that it blocks — that behaviour is desirable — but that sites often make the browser wait for far more CSS than the initial view needs. A single large stylesheet containing the styles for the entire site blocks the first paint even though only a fraction of those rules apply to what is above the fold. The solution, covered below, is to separate the small amount of critical CSS the visible area needs from the rest.
Why JavaScript blocks parsing
JavaScript blocks differently. When the HTML parser encounters a plain <script src="..."> in the <head>, it stops parsing the HTML, downloads the script, executes it, and only then resumes. It does this because the script might modify the document (for example with document.write), so the browser cannot safely continue until it knows what the script did.
The consequence is a double delay: the parser pauses while the script downloads, and pauses again while it executes — and execution can be slow, since the browser must parse and compile the code too. A handful of synchronous scripts in the head can hold up the entire page before any content appears. Unlike CSS, this blocking is almost always unnecessary, because most scripts do not need to run before the page renders. That is what defer and async exist to fix.
The fixes, summarised
Here is the toolkit at a glance, with what each technique addresses.
| Technique | What it fixes | When to use |
|---|---|---|
defer on scripts | Stops JS blocking parsing; runs after parse, in order | The safe default for most scripts |
async on scripts | Stops JS blocking parsing; runs as soon as ready | Independent scripts where order does not matter |
Move scripts to end of <body> | Lets HTML parse and paint before scripts run | Simple alternative to defer |
| Inline critical CSS | Removes the blocking CSS request for the visible area | Above-the-fold styles |
| Load rest of CSS non-blocking | Stops the full stylesheet blocking first paint | Below-the-fold and non-critical styles |
media attribute on CSS | Marks print/conditional CSS as non-blocking | Print styles, specific media queries |
preload critical resources | Fetches genuinely critical files early | The LCP image, a key font |
| Remove unused CSS/JS | Less to download and block on at all | Everywhere — audit with Coverage |
How to eliminate render-blocking JavaScript
This is usually the quickest win, because the blocking is rarely necessary.
Add defer to your scripts. A <script defer src="..."> downloads in parallel without pausing the parser and runs after the HTML is fully parsed, in document order. For the large majority of scripts this is exactly the behaviour you want: non-blocking and order-preserving. It is the safe default.
Use async for independent scripts. A <script async src="..."> also downloads without blocking, but runs as soon as it finishes — which can interrupt parsing and does not preserve order. Reserve it for scripts that do not depend on others and that nothing depends on, such as some analytics tags.
Or move scripts to the end of the <body>. A script placed just before the closing </body> tag does not block the initial parse and paint, because the HTML above it has already been processed. This is a simple, robust approach that predates defer and still works well.
The broader point is that the amount of JavaScript matters too — less script means less to download and execute even when deferred. The full treatment is in how to reduce JavaScript and speed up your site.
How to eliminate render-blocking CSS
CSS needs a subtler approach, because you cannot simply defer all of it without causing the flash of unstyled content it is designed to prevent. The standard technique is to split it:
Inline the critical CSS. Identify the minimal set of rules needed to style the above-the-fold content and place them directly inside a <style> block in the <head>. Because they are inline, they require no separate network request and do not block on a download — the browser can paint the visible area almost immediately.
Load the rest non-blocking. Load the full stylesheet in a way that does not block rendering — a common pattern uses <link rel="preload" as="style"> with an onload handler that switches it to a regular stylesheet, or the media-toggle trick — so the complete styles arrive in the background without holding up first paint.
Use media attributes for conditional CSS. A stylesheet that only applies under certain conditions can be marked so it does not block rendering for everyone. The classic example is print styles: <link rel="stylesheet" href="print.css" media="print"> tells the browser this CSS is only for printing, so it loads without blocking the on-screen render. The same idea applies to stylesheets scoped to specific media queries.
Remove unused CSS. Large stylesheets often contain rules for components not present on the current page. The DevTools Coverage panel shows how much of each stylesheet goes unused; trimming or splitting it means there is simply less render-blocking CSS to process.
Preloading what genuinely matters
Eliminating blocking is about removing unnecessary delays; preload is the complement — telling the browser to fetch a genuinely critical resource early so it is ready when needed. The best example is the LCP image: <link rel="preload" as="image" href="hero.avif" fetchpriority="high"> ensures the browser discovers and fetches it immediately rather than late. The same can help a critical web font, so text renders without a delay or a reflow. The discipline here is restraint: preload only the handful of resources on the critical path. Preloading everything is self-defeating, because it floods the network and delays the very things you meant to prioritise. Use it surgically, in tandem with the blocking fixes above.
How render-blocking ties to FCP and LCP
It is worth being precise about which metrics this work moves. First Contentful Paint (FCP) marks the first moment anything is painted — the most direct victim of render-blocking, since blocking resources are by definition what stands between the HTML arriving and the first paint. Clearing them almost always improves FCP. Largest Contentful Paint (LCP) is also affected, because the largest element cannot render while the browser is blocked, and render delay is one of LCP's four sub-parts — see what is Largest Contentful Paint and how to improve it. A page with heavy render-blocking often shows a large gap between when the resource arrives and when it paints; removing the blockers closes that gap. Both metrics feed Core Web Vitals, so the work pays off in the scores Google actually uses.
How to find render-blocking resources
You do not have to guess which resources are blocking; the tools name them.
- Lighthouse / PageSpeed Insights — run the audit and read "Eliminate render-blocking resources." It lists the exact CSS and JavaScript files delaying first paint, with the estimated time you would save by deferring or inlining each. This is the fastest route to a prioritised list.
- Chrome DevTools → Network panel — record a load and read the waterfall. Resources that complete before the first paint marker, and that the parser waited on, are your blockers. You can see at a glance whether a script or stylesheet sits on the critical path.
- Chrome DevTools → Coverage panel — open it, reload, and see what percentage of each CSS and JavaScript file is unused. High unused percentages on render-blocking files point straight at trimming and splitting opportunities.
- Chrome DevTools → Performance panel — record a load to see exactly when FCP and LCP occur relative to when blocking resources finish, confirming the cause-and-effect.
Attack the largest blockers first. As with all performance work, the returns are steep at the top of the list and shallow at the bottom — clearing the one or two biggest render-blocking resources usually delivers most of the FCP improvement. A broader audit such as StackOptic will also surface render-blocking issues as part of a wider performance report, alongside the other levers.
A worked example
Suppose Lighthouse reports a slow FCP and flags two render-blocking resources: a 120KB site-wide stylesheet and a synchronous analytics script in the <head>. You make three changes. First, you add defer to the analytics script (or move it to the end of the body), so it no longer pauses parsing. Second, you inline the ~8KB of critical CSS the above-the-fold view needs and load the full stylesheet non-blocking. Third, the Coverage panel showed 60% of that stylesheet was unused on this page, so you split it and ship less. The browser now parses the HTML without interruption, paints the visible area from inline CSS almost immediately, and FCP drops sharply — with LCP following, since the hero element can now render as soon as its image arrives. This is the typical shape of render-blocking work: a few targeted changes to the critical path, a large and visible improvement.
Common mistakes
- Leaving synchronous scripts in the
<head>whendeferwould cost nothing and unblock the parser. - Shipping one giant stylesheet that blocks first paint even though most of it styles content below the fold.
- Inlining too much CSS, bloating the HTML so the document itself becomes slow to download.
- Preloading everything, which congests the network and delays the genuinely critical resources.
- Optimising the lab score while ignoring whether real-user FCP and LCP in the field actually improved.
Why it is worth doing
Render-blocking is one of the highest-leverage areas in performance because it sits at the very start of the loading sequence: until it is cleared, nothing renders, so every other optimisation downstream is gated behind it. Fixing it makes the page feel faster in the most visible way possible — content appears sooner — and the changes are mostly low-risk and one-time: add defer, inline a little CSS, trim what is unused. The effect is most dramatic on slow connections and modest devices, which is exactly where Core Web Vitals are measured and where many real users live. Clear the critical path and the whole page benefits.
Go deeper
- The big picture, in priority order: how to make your website load faster.
- The loading metric it improves: what is Largest Contentful Paint and how to improve it.
- The full set of metrics: Core Web Vitals explained.
- The expensive blocking resource: how to reduce JavaScript and speed up your site.
Want to see which resources block your first paint, alongside performance, SEO and security? Analyse any URL with StackOptic — free, no sign-up.
Frequently asked questions
What is render-blocking?
Render-blocking refers to resources — chiefly CSS and synchronous JavaScript in the page head — that the browser must download and process before it can render any content. Because the browser needs CSS to know how to paint and pauses HTML parsing for blocking scripts, these resources delay first paint: the page stays blank until they finish. Eliminating or deferring render-blocking resources lets the browser show content sooner, which is one of the most effective ways to make a page feel faster.
Why is CSS render-blocking?
The browser blocks rendering on CSS by design, to avoid a flash of unstyled content. If it painted the page before the stylesheets loaded, users would see raw, unstyled HTML that then jumps as styles apply. So the browser waits for CSS in the head to download and parse before painting anything. The fix is not to remove CSS but to send only the small amount needed for the visible area first (critical CSS) and load the rest without blocking.
How do I stop JavaScript from blocking rendering?
Add the defer or async attribute to your script tags. By default a script in the head pauses HTML parsing while it downloads and executes. defer downloads it in parallel and runs it after parsing finishes, in order; async downloads in parallel and runs as soon as ready, without guaranteeing order. defer is the safe default for most scripts. You can also move non-critical scripts to the end of the body so they no longer block the initial parse and paint.
What is critical CSS?
Critical CSS is the minimal set of style rules needed to render the content visible in the initial viewport (above the fold). The technique is to inline that small block of CSS directly in the page head so it requires no separate download and does not block rendering, then load the full stylesheet asynchronously so it does not delay first paint. This lets the browser paint the visible area almost immediately while the rest of the styles arrive in the background.
How do I find render-blocking resources?
Run the page through Lighthouse or PageSpeed Insights and look at the 'Eliminate render-blocking resources' audit, which lists the specific CSS and JavaScript files delaying first paint along with the estimated time you could save. In Chrome DevTools, the Network panel shows the request waterfall so you can see what loads before first paint, and the Coverage panel reveals how much of each render-blocking file is actually unused and could be trimmed.
Analyse any website with StackOptic
Get the full technology stack, performance, security and SEO report in seconds — free.
Analyse a websiteRelated articles
How to Test Your Website Speed (Tools and Metrics)
Which tools to use, lab versus field data, and the metrics that matter. Test your site speed with PageSpeed Insights, Lighthouse, WebPageTest and DevTools.
How to Optimize CSS for Faster Pages
CSS is render-blocking, so bloated stylesheets delay your page. How to minify, cut unused CSS, inline critical CSS and defer the rest to speed up FCP and LCP.
What Are Gzip and Brotli Compression (and How to Enable Them)?
Gzip and Brotli shrink HTML, CSS and JavaScript before they are sent. What each is, how Brotli compares to Gzip, how to verify it, and how to enable it.