Reference guide · performance · Published 2026-08-16 · 3 min read
Render-blocking CSS vs JavaScript explained
Understand the difference between render-blocking CSS and parser-blocking JavaScript and how each delays first paint and LCP.
- ·How each blocks
- ·The key difference
- ·Fixing in the right order
Render-blocking resources are the files a browser must download and process before it can paint a single pixel. CSS and JavaScript both block, but they block in different ways, and knowing which is which tells you how to fix each one. On most slow pages CSS is the primary render blocker, while a synchronous script is more often the parser blocker that keeps the HTML from even finishing.
How each blocks
A stylesheet in the head is render-blocking by default. The browser needs a complete CSS object model, or CSSOM, before it can compute the style of any element, so it refuses to paint until the relevant stylesheet loads and parses. This is deliberate: without it the page would flash unstyled content. The cost is that a large, slow stylesheet, or a chain of @import rules inside one, holds back every other paint.
A synchronous script element in the head is parser-blocking. The HTML parser stops, fetches the script, executes it, and only then resumes building the document. Because the DOM stops growing, the render tree cannot update either, so a synchronous script is effectively both parser-blocking and render-blocking in practice. Scripts marked async or defer download in parallel and do not pause the parser, and type="module" scripts defer by default.
The key difference
The mental model that matters is this:
- CSS with no matching fallback blocks the paint, not the parser. The DOM can keep growing while the stylesheet downloads, but no frame is shown until paint is allowed.
- A synchronous script blocks the parser first. The DOM itself cannot finish, so no frame can be drawn regardless of styles.
asyncanddefermove the script off the critical path.asyncruns as soon as it is ready, in no guaranteed order.deferruns after the document is parsed, in source order.- The
fetchpriorityhint reorders the script or stylesheet in the download queue but does not remove its blocking status.
Because of those differences, a render-blocking CSS file delays first contentful paint even on a page with no JavaScript, while heavy synchronous JavaScript tends to show up more as a stall in when the page is interactive. Both can push out largest contentful paint, or LCP, since they sit directly on the critical rendering path.
Fixing in the right order
The effective order is to address CSS first, because it blocks paint for every user, then the parser-blocking scripts.
For CSS, inline the small set of styles required for the first paint, at a few kilobytes, directly in the head, and load the rest non-blocking using a preload link that becomes a stylesheet once loaded, or a media-split stylesheet that only blocks for the matching device. Use the coverage report in DevTools to find the chunk of CSS that is genuinely critical and the chunk that is unused.
For JavaScript, add defer to any script that needs the DOM or depends on another script. Use async for independent scripts such as analytics that can run out of order. Move anything that is not needed for the first paint below the fold or out of the head altogether. The network panel confirms the result: blocking stylesheets and scripts stop appearing before the first paint mark.
After each change, re-test on a fresh full trace. Both audits share a name, so it is easy to fix only one and watch the other stay red. See render blocking resources, the critical rendering path and defer vs async for the accompanying detail.