Performance
INP and long tasks: why the page stops answering
A long task over 50 ms blocks every interaction. Three loads under mobile emulation gave one task of 513 ms: how to find it and split it.
Key points
- A long task exceeds 50 ms of busy main thread and delays every input that arrives during the block.
- Across our three mobile loads the longest task lasted 513 ms, with TBT between 400 and 508 ms.
- INP adds input delay, processing and presentation delay: fixing one part alone is not enough.
- Lab TBT explains the problem, field INP decides whether the page passes.
A long task is a main-thread block over 50 ms: when you find one at 513 ms, every tap during that block waits. INP measures how long it waits, with the good threshold at 200 ms.
The three phases of an interaction
| Phase | What happens | Where it shows | Remedy |
|---|---|---|---|
| Input delay | The main thread is busy when the tap arrives | event with a high processingStart | shorter tasks |
| Processing | The handler runs too much code | the duration of the event | less work per handler |
| Presentation delay | The browser computes style and layout | from event end to the next frame | fewer nodes and less CSS |
The sum of the three phases is what counts. During loading the input delay is often the largest part, and no handler optimisation touches it.
Finding long tasks
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(Math.round(entry.duration), "ms at", Math.round(entry.startTime));
}
}).observe({ type: "longtask", buffered: true });
Paste the snippet into the browser console and reload the page: with buffered the tasks emitted before the observer come back too. The longtask type is exposed by Chrome and Edge; Safari and Firefox do not return it, so there the Performance panel trace is the better tool.
How to split them
- Split the work into blocks under 50 ms and hand control back with scheduler.yield() or setTimeout(0). Source: web.dev, CC BY 4.0.
- Defer non-critical JavaScript with dynamic import() and type=module.
- Start animations only when they are visible: an off-screen marquee burns frames while showing nothing.
- Never read layout inside a handler after a write: getBoundingClientRect forces a synchronous reflow.
The limitation to name
Lab TBT is not INP: it sums the blocked time during loading and says nothing about the latency of a real tap. A site with high TBT can show good INP when nobody interacts while it loads; real INP needs field data, such as the records the beacon in client/src/rum.ts collects.
Where to continue
The EneUI catalogue indexes 111,985 components, 1,556 blocks and the record testimonials with marquee: a marquee that always animates keeps the main thread busy even off screen. For the metric read INP: what it measures, for the full picture what Core Web Vitals are, and for the measured comparison React libraries 2026.
Frequently asked questions
How long must a task last to be a long task?
Over 50 ms of busy main thread. The threshold is a convention of the measuring tools, not a browser limit.
Are TBT and INP the same thing?
No. TBT sums the blocked time during loading in the lab, INP measures the latency of the response to a real input. The first explains, the second decides.
How much does a 513 ms long task raise INP?
It raises to about 513 ms the wait of every input that arrives during the block. The final INP is the 75th percentile of interactions, not the worst value.
How do I split a long task?
Cut the work into blocks under 50 ms and hand control back between them with scheduler.yield(). Defer with dynamic import() whatever the first paint does not need.
Why is my INP good in Lighthouse?
Lighthouse does not measure INP, it reports TBT. Real responsiveness comes from field data or from a trace of the interactions.