The browser's main thread parses HTML and CSS, runs JavaScript, handles user input, and paints frames. When a single task on this thread runs longer than 50ms it is classified as a "long task" and blocks input, which directly hurts Interaction to Next Paint (INP). The "Minimize main-thread work" audit sums the total time spent on script evaluation, style and layout, painting, and parsing during page load.
content-visibility: auto can skip rendering work for offscreen sections, often cutting style and layout time by 30 to 50 percent.Each cause maps to a specific Core Web Vital. The table below pairs the cause with the metric it hurts and the canonical fix.
| Cause | Metric Affected | Fix Pattern |
|---|---|---|
| Large JavaScript bundles | TBT, INP, LCP | Code-split with dynamic import(), tree-shake, defer non-critical JS |
| Long synchronous tasks (>50ms) | INP, TBT | Yield with scheduler.yield() or setTimeout(fn, 0), chunk work |
| Layout thrashing (read/write/read DOM) | INP, CLS | Batch reads, then batch writes; use requestAnimationFrame |
| Unoptimized React renders | INP | React.memo, useMemo, list virtualization |
| Heavy CSS recalc on long pages | LCP, INP | content-visibility: auto for offscreen sections |
| CPU-bound computation | INP, TBT | Move to a Web Worker |
| Frequent scroll/resize handlers | INP | Debounce or throttle; use requestIdleCallback |
import() for routes and modals.await scheduler.yield() (Chrome 129+) or await new Promise(r => setTimeout(r, 0)).content-visibility: auto to long lists, comments, and footers.// Bad: one 800ms task that blocks every input
function processAll(items) {
for (const item of items) heavyWork(item);
}
// Good: yield every 5ms so the browser can handle input
async function processAll(items) {
let deadline = performance.now() + 5;
for (const item of items) {
heavyWork(item);
if (performance.now() > deadline) {
await scheduler.yield(); // Chrome 129+
deadline = performance.now() + 5;
}
}
}// main.js
const worker = new Worker(new URL('./parser.worker.js', import.meta.url), { type: 'module' });
worker.postMessage(largePayload);
worker.onmessage = (e) => render(e.data);
// parser.worker.js
self.onmessage = (e) => {
const result = parseAndIndex(e.data); // CPU-bound, never touches the DOM
self.postMessage(result);
};.comment, .product-card {
content-visibility: auto;
contain-intrinsic-size: 0 240px; /* reserve space to avoid CLS */
}setTimeout(fn, 0) as the only chunking strategy — it still posts macrotasks; scheduler.yield() resumes faster.offsetHeight inside a write loop, forcing forced synchronous layout on every iteration.content-visibility: auto without contain-intrinsic-size, which causes scrollbar jumping.performance.measure() markers in code.For deeper context, see our guides on Total Blocking Time and long tasks.
It is a diagnostic that sums every category of main-thread work during page load — script evaluation, style and layout, parsing, painting, and garbage collection — and flags pages where the total exceeds roughly 4 seconds on a simulated mid-tier mobile device.
Total Blocking Time only counts the portion of long tasks above the 50ms threshold between FCP and TTI. Main-thread work is the total time, including short tasks.
Yes. INP measures the longest input latency the user experienced, which is dominated by main-thread blocking — long tasks, expensive event handlers, and rendering work after the handler runs.
No. Workers cannot touch the DOM, so the main thread still does layout, paint, and event handling. Workers help with pure computation but you also need to optimize handlers and rendering.
Use it for genuinely deferrable work such as analytics flushing or preloading. It is not supported in Safari, so use the safari-aware scheduler.postTask with priority: 'background' when available.
Almost always at initial load, but it can hurt INP if a user interaction triggers a large chunk download. Preload critical chunks with <link rel="modulepreload">.
It tells the browser to skip rendering work — style, layout, and paint — for elements that are not in the viewport. On long pages this can cut main-thread render time by 30 to 50 percent.
Indirectly, yes. AI search engines preferentially cite pages that already rank well in traditional search, and INP is a Core Web Vital ranking signal — so heavy main-thread work that delays input response can lower rankings, which lowers citation odds. Long main-thread tasks also delay content rendering, sometimes past the timeouts AI crawlers use when fetching a page.
Minimizing main-thread work means cutting long tasks below 50ms, splitting bundles, moving computation to Web Workers, and skipping offscreen rendering with content-visibility. Profile in Chrome DevTools, fix the longest tasks first, and validate with field INP data — the audit fails when total main-thread work exceeds about 4 seconds on a mid-tier mobile, so aim for under 2 seconds. Run a Greadme deep scan to identify pages with heavy main-thread work across your site.