Back to Browser Internals
Browser Internals
easy
mid

What resources block the Critical Rendering Path, and how do you unblock them?

Critical rendering path = HTML parse → CSSOM build → DOM + CSSOM merge into render tree → layout → paint. Blockers: render-blocking CSS (parser-blocking and render-blocking), parser-blocking sync `<script>` in head, large fonts (FOIT), heavy synchronous JS. Fixes: minimal critical CSS inline, `defer`/`async` scripts, font-display: swap, code split, preconnect.

4 min read·~8 min to think through

The browser doesn't paint pixels until it has a render tree. Anything that delays the render tree blocks the first paint.

The path

ts
HTML bytes → Tokenize → DOM
CSS bytes → Tokenize → CSSOM
DOM + CSSOM → Render Tree → Layout → Paint

What blocks

  1. CSS is render-blocking by default. The browser won't paint until CSSOM is built. <link rel="stylesheet"> in <head> delays first paint.
  2. CSS is also parser-blocking for JS that follows it (since scripts can query computed styles).
  3. Sync <script> is parser-blocking — HTML parsing stops while the script downloads + executes.
  4. @import in CSS — sequential, each adds a roundtrip. Avoid.
  5. Fonts — if not declared, the browser may show FOIT (flash of invisible text) until the font loads.

Fixes

Inline critical CSS

The CSS for above-the-fold content goes in <style> inline; the rest loads async:

html
<style>/* critical, < 14kb */</style>
<link rel="preload" as="style" href="main.css" onload="this.rel='stylesheet'">

defer / async on scripts

html
<script defer src="app.js"></script>     <!-- downloaded async, executed after parse -->
<script async src="analytics.js"></script>  <!-- downloaded async, executed ASAP -->
<script type="module" src="..."></script>   <!-- modules are defer by default -->

defer preserves order; async doesn't. Use defer for app code, async for independent scripts (analytics).

Font loading

css
@font-face {
  font-family: "Inter";
  src: url("inter.woff2") format("woff2");
  font-display: swap;     /* show fallback immediately, swap when loaded */
}

Plus <link rel="preload" as="font" href="inter.woff2" crossorigin> for above-the-fold fonts.

Preconnect / dns-prefetch

html
<link rel="preconnect" href="https://api.example.com">
<link rel="dns-prefetch" href="//cdn.example.com">

Saves up to 200ms on cold connections — see [[dns-resolution-tcp-tls-the-request-lifecycle]].

Code splitting

Don't ship the whole app for the landing route. Lazy-import non-critical modules.

Tools

  • Lighthouse flags render-blocking resources.
  • DevTools Performance shows the rendering waterfall.
  • WebPageTest filmstrip shows when first paint actually happened.

Interview framing

"Critical rendering path: HTML→DOM, CSS→CSSOM, combine into render tree, layout, paint. CSS is render-blocking by default; sync scripts in head are parser-blocking. Inline above-the-fold critical CSS, defer/async other scripts, use font-display: swap with preload for above-the-fold fonts, preconnect to third-party origins. Code split so first paint loads minimum. Measure with Lighthouse and DevTools Performance."

Follow-up questions

  • How do defer and async differ?
  • What's font-display: swap vs optional?
  • When does CSS block JS execution?

Common mistakes

  • Sync <script> in head.
  • @import inside CSS files.
  • Shipping all CSS upfront instead of route-specific.
  • No font-display, leading to FOIT.

Performance considerations

  • First paint is gated by CSSOM build + render tree. Every blocker costs LCP. Critical CSS inlining + async fonts + deferred scripts are the durable wins.

Edge cases

  • FOUC (flash of unstyled content) when CSS loads after HTML.
  • Critical CSS extraction tooling drift.
  • CDN regions affect preconnect benefit.

Real-world examples

  • Lighthouse audits, Next.js automatic critical CSS extraction, WebPageTest filmstrip analysis.

Senior engineer discussion

Seniors think in terms of the render tree timeline, identify what blocks LCP specifically (often the LCP image, not just CSS), and use RUM to validate fixes in production.

Related questions