Skip to Content
LoadingFind render-blocking resources

Find render-blocking resources

Chrome 107+ Edge 107+ Firefox Safari Details

Identifies resources that block the browser from rendering the page. These resources must be fully downloaded and processed before the browser can display any content, directly impacting First Contentful Paint (FCP) and Largest Contentful Paint (LCP).

Why this matters

Render-blocking resources are the primary cause of slow initial page renders. Users see a blank white screen while CSS and JavaScript files download and parse. On slow connections or mobile networks, this can add several seconds to your load time. Eliminating or deferring render-blocking resources is one of the highest-impact optimizations you can make.

What are render-blocking resources?

Resource TypeWhen it blocks rendering
CSSAll <link rel="stylesheet"> in <head> block rendering by default
JavaScriptScripts without async or defer in <head> block parsing and rendering
Fonts@font-face fonts block text rendering until loaded

Impact on performance

  • Delays First Contentful Paint (FCP)
  • Can delay Largest Contentful Paint (LCP)
  • Users see a blank page while resources load
  • Particularly harmful on slow connections

Render-blocking timeline

Switch between the render-blocking and optimized setups, then step through to see when the first paint can happen in each. Open demo in a new tab ↗

Browser support: The renderBlockingStatus property is currently Chromium-only (Chrome 107+, Edge 107+, Opera 93+). Firefox and Safari don’t support this API yet.

Snippet

Run it from an AI agent: the prompt uses the CLI to measure any URL and report the result.

// Find render-blocking resources
// https://webperf-snippets.nucliweb.net

(() => {
  // Check browser support
  const testEntry = performance.getEntriesByType("resource")[0];
  if (testEntry && !("renderBlockingStatus" in testEntry)) {
    console.log(
      "%c⚠️ renderBlockingStatus not supported in this browser",
      "color: #f59e0b; font-weight: bold;"
    );
    console.log("This API is currently Chromium-only (Chrome 107+, Edge 107+).");
    console.log("Try using Chrome or Edge to analyze render-blocking resources.");
    return { script: "Find-render-blocking-resources", status: "unsupported", error: "renderBlockingStatus API requires Chrome 107+" };
  }

  const blockingResources = performance
    .getEntriesByType("resource")
    .filter((entry) => entry.renderBlockingStatus === "blocking")
    .map((entry) => {
      const url = new URL(entry.name);
      return {
        name: entry.name,
        shortName: url.pathname.split("/").pop() || url.pathname,
        type: entry.initiatorType,
        startTime: entry.startTime,
        duration: entry.duration,
        responseEnd: entry.responseEnd,
        size: entry.transferSize || 0,
      };
    })
    .sort((a, b) => b.responseEnd - a.responseEnd);

  const lastBlockingEnd = blockingResources.length
    ? Math.max(...blockingResources.map((r) => r.responseEnd))
    : 0;
  const totalSizeBytes = blockingResources.reduce((sum, r) => sum + r.size, 0);
  const byType = blockingResources.reduce((acc, r) => {
    acc[r.type] = (acc[r.type] || 0) + 1;
    return acc;
  }, {});

  console.group("%c🚧 Render-Blocking Resources", "font-weight: bold; font-size: 14px;");

  if (blockingResources.length === 0) {
    console.log(
      "%c✅ No render-blocking resources found!",
      "color: #22c55e; font-weight: bold;"
    );
    console.log("The page has no resources blocking initial render.");
    console.log("");
    console.log("%c💡 This could mean:", "font-weight: bold;");
    console.log("   • CSS is inlined or loaded asynchronously");
    console.log("   • Scripts use async or defer attributes");
    console.log("   • Critical resources are optimized");
  } else {
    // Summary
    console.log(
      `%c⚠️ Found ${blockingResources.length} render-blocking resource(s)`,
      "color: #ef4444; font-weight: bold; font-size: 14px;"
    );
    console.log("");

    console.log("%c📊 Impact Summary:", "font-weight: bold;");
    console.log(`   Rendering blocked until: ${lastBlockingEnd.toFixed(0)}ms`);
    console.log(`   Total blocking resources: ${blockingResources.length}`);
    if (totalSizeBytes > 0) {
      const sizeKB = (totalSizeBytes / 1024).toFixed(1);
      console.log(`   Total size: ${sizeKB} KB`);
    }
    console.log(`   By type: ${Object.entries(byType).map(([k, v]) => `${k} (${v})`).join(", ")}`);

    // Timeline visualization
    console.log("");
    console.log("%c⏱️ Timeline (sorted by completion):", "font-weight: bold;");

    const formatBar = (end, max) => {
      const pct = (end / max) * 100;
      const width = Math.round(pct / 5);
      return "█".repeat(width) + "░".repeat(20 - width);
    };

    blockingResources.forEach((r) => {
      const bar = formatBar(r.responseEnd, lastBlockingEnd);
      const sizeInfo = r.size > 0 ? ` (${(r.size / 1024).toFixed(1)} KB)` : "";
      console.log(
        `   ${r.type.padEnd(8)} ${r.responseEnd.toFixed(0).padStart(5)}ms ${bar} ${r.shortName}${sizeInfo}`
      );
    });

    // Table with details
    console.log("");
    console.log("%c📋 Details:", "font-weight: bold;");
    const tableData = blockingResources.map((r) => ({
      Type: r.type,
      "Response End": r.responseEnd.toFixed(0) + "ms",
      Duration: r.duration.toFixed(0) + "ms",
      Size: r.size > 0 ? (r.size / 1024).toFixed(1) + " KB" : "N/A",
      Resource: r.name.length > 60 ? "..." + r.name.slice(-57) : r.name,
    }));
    console.table(tableData);

    // Recommendations
    console.log("");
    console.log("%c📝 How to fix:", "color: #3b82f6; font-weight: bold;");
    console.log("");

    if (byType.script) {
      console.log("%c   Scripts:", "font-weight: bold;");
      console.log("   • Add 'defer' for scripts that don't need to run immediately");
      console.log("   • Add 'async' for independent scripts (analytics, etc.)");
      console.log("   • Move non-critical scripts to end of <body>");
      console.log('%c   <script src="app.js" defer></script>', "font-family: monospace; color: #22c55e;");
      console.log("");
    }

    if (byType.link || byType.css) {
      console.log("%c   CSS:", "font-weight: bold;");
      console.log("   • Inline critical CSS in <head>");
      console.log("   • Load non-critical CSS asynchronously with media='print' trick");
      console.log("   • Use <link rel='preload'> for critical stylesheets");
      console.log('%c   <link rel="preload" href="style.css" as="style" onload="this.rel=\'stylesheet\'">', "font-family: monospace; color: #22c55e;");
      console.log("");
    }

    if (byType.font) {
      console.log("%c   Fonts:", "font-weight: bold;");
      console.log("   • Use font-display: swap in @font-face");
      console.log("   • Preload critical fonts");
      console.log("   • Consider system font stack for initial render");
      console.log('%c   <link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>', "font-family: monospace; color: #22c55e;");
      console.log("");
    }
  }

  console.groupEnd();

  return {
    script: "Find-render-blocking-resources",
    status: "ok",
    count: blockingResources.length,
    details: {
      totalBlockingUntilMs: Math.round(lastBlockingEnd),
      totalSizeBytes,
      byType,
    },
    items: blockingResources.map((r) => ({
      type: r.type,
      url: r.name,
      shortName: r.shortName,
      responseEndMs: Math.round(r.responseEnd),
      durationMs: Math.round(r.duration),
      sizeBytes: r.size,
    })),
    issues: blockingResources.length > 0
      ? [{ severity: "error", message: `${blockingResources.length} render-blocking resource(s) delay rendering until ${Math.round(lastBlockingEnd)}ms` }]
      : [],
  };
})();

Understanding the results

Summary metrics

MetricDescription
Rendering blocked untilTime when the last blocking resource finishes loading - rendering can start after this point
Total blocking resourcesNumber of resources blocking initial render
Total sizeCombined transfer size of all blocking resources
By typeBreakdown by resource type (script, link/css, font)

Timeline visualization

Shows each resource with:

  • Resource type
  • When it finished loading (responseEnd)
  • Visual bar showing relative timing
  • Filename and size

Resources are sorted by completion time (latest first), so you can see which resource is the last to complete and thus determines when rendering can start.

Optimization decision tree

Decision tree for a render-blocking resource: critical CSS is inlined, other CSS loads asynchronously, critical small scripts are inlined, other critical scripts move to the end of body, non-critical scripts use defer, and fonts use font-display swap plus preloadConnections: Render-Blocking Resource to Resource Type?; Resource Type? to Critical for initial render? (CSS); Resource Type? to Critical for initial render? (JavaScript); Resource Type? to Use font-display: swap (Font); Critical for initial render? to Inline critical CSS in <head> (Yes); Critical for initial render? to Load asynchronously media='print' onload trick (No); Critical for initial render? to Very small < 1KB? (Yes); Critical for initial render? to Add defer attribute (No); Very small < 1KB? to Inline the script (Yes); Very small < 1KB? to Move to end of <body> (No); Use font-display: swap to Preload critical fonts.Render-Blocking ResourceResource Type?Critical forinitial render?Critical forinitial render?Use font-display: swapInline critical CSSin <head>Load asynchronouslymedia='print' onload trickVery small< 1KB?Add defer attributeInline the scriptMove to end of <body>Preload critical fontsCSSJavaScriptFontYesNoYesNoYesNo
Decision tree for a render-blocking resource: critical CSS is inlined, other CSS loads asynchronously, critical small scripts are inlined, other critical scripts move to the end of body, non-critical scripts use defer, and fonts use font-display swap plus preloadConnections: Render-Blocking Resource to Resource Type?; Resource Type? to Critical for initial render? (CSS); Resource Type? to Critical for initial render? (JavaScript); Resource Type? to Use font-display: swap (Font); Critical for initial render? to Inline critical CSS in <head> (Yes); Critical for initial render? to Load asynchronously media='print' onload trick (No); Critical for initial render? to Very small < 1KB? (Yes); Critical for initial render? to Add defer attribute (No); Very small < 1KB? to Inline the script (Yes); Very small < 1KB? to Move to end of <body> (No); Use font-display: swap to Preload critical fonts.Render-BlockingResourceResourceType?Critical forinitial render?Critical forinitial render?Use font-display:swapInline criticalCSSin <head>Loadasynchronouslymedia='print'onload trickVery small< 1KB?Add deferattributeInline the scriptMove to end of<body>Preload criticalfontsCSSJavaScriptFontYesNoYesNoYesNo

Common render-blocking patterns

Scripts in <head> without defer/async

<!-- ❌ Render-blocking --> <head> <script src="app.js"></script> </head> <!-- ✅ Non-blocking --> <head> <script src="app.js" defer></script> </head>

External stylesheets

<!-- ❌ Render-blocking (but often necessary) --> <link rel="stylesheet" href="styles.css"> <!-- ✅ Preload + async load for non-critical CSS --> <link rel="preload" href="non-critical.css" as="style" onload="this.rel='stylesheet'"> <noscript><link rel="stylesheet" href="non-critical.css"></noscript>

Web fonts

/* ❌ Blocks text rendering */ @font-face { font-family: 'MyFont'; src: url('font.woff2') format('woff2'); } /* ✅ Shows fallback font immediately */ @font-face { font-family: 'MyFont'; src: url('font.woff2') format('woff2'); font-display: swap; }

Browser support

FeatureChromeEdgeFirefoxSafari
PerformanceResourceTiming.renderBlockingStatus (opens in a new tab) 107 107

Source: MDN browser compatibility data (opens in a new tab), version 8.1.4.

Further reading

Last updated on
MIT 2026 © Joan León | @nucliweb