Improve Interaction to Next Paint by reproducing a slow interaction and finding whether the delay occurs before its handlers, inside them or before the next frame appears. Remove unnecessary work, split remaining work where correctness allows, and reduce expensive rendering. Confirm the result with interactive recordings and real-user measurements, not just a better page-load score.
INP is good at 200 milliseconds or less at the 75th percentile. It describes responsiveness during a visit, so the action that determines it may happen well after the page first looks ready. A product filter or navigation control can be the problem even when loading is quick.
Understand what INP measures and what it leaves out
INP evaluates qualifying clicks, taps and keyboard interactions. It considers the delay until the next paint after the relevant interaction work. It is not simply event-handler execution time, and it is not a stopwatch for the entire business process that follows a click.
For example, a search interface can show an immediate loading state while an asynchronous request retrieves results. Prompt feedback can improve responsiveness, but it does not make the network response faster. Measure task completion as well when the visitor's goal depends on that later result.
The metric generally reflects the slowest interaction observed during a page visit, with an outlier allowance for visits containing many interactions. Visits without qualifying interaction have no INP measurement. Do not replace missing data with zero or interpret it as evidence that the page reacts instantly.
INP replaced First Input Delay as a Core Web Vital in March 2024. FID covered only the initial input delay of the first qualifying interaction; it did not describe the complete response across the visit. Older FID-focused advice can therefore miss a slow later interaction or expensive presentation work.
The beginner's Core Web Vitals guide explains the thresholds and percentile assessment in context. Keep those population-level results separate from one deliberately reproduced interaction in a development environment.
Split the latency into three phases
The three phases suggest different investigations. Google's INP optimization guidance uses this breakdown to connect the metric to the work on the main thread.
| Phase | What happens | What to inspect |
|---|---|---|
| Input delay | Input waits before handlers begin | Existing long tasks, timers and script activity |
| Processing duration | Interaction handlers execute | Computation, synchronous work and repeated handlers |
| Presentation delay | Browser prepares and displays the next frame | Style, layout, paint and large DOM updates |
A handler can be short and still feel slow because another script blocks its start. A handler can finish quickly while the resulting layout takes substantial work. Reading only one function's duration will miss both cases.
Begin with the longest phase in the problematic trace. After improving it, inspect the whole interaction again. A change that shifts work from one phase into another may not reduce the total delay, while a smaller update can reduce more than one phase at once.
Record the interaction in a representative state
Use the Performance panel in Chrome DevTools to record the action itself. Start recording, reproduce the interaction, then stop and inspect its timeline. Include the state that matters: an open menu, a populated cart, an expanded list or a page that has accumulated content after scrolling.
A fresh empty page may not reproduce a problem that emerges after several updates. Conversely, a browser full of unrelated extensions can introduce work your visitors do not receive. Keep the environment controlled while preserving the parts of the real journey that affect the result.
Test on hardware representative of your audience when possible. CPU throttling can help expose sensitivity, but it is a simulation and should be recorded as such. Do not present a local throttled recording as measured behavior on a particular physical phone unless you actually tested that phone.
Save the interaction target, the conditions and the relevant call stack. A useful bug report says which control responds slowly and what work occupies the interval. A screenshot of a red aggregate score leaves the next person to repeat the investigation.
Add attribution where aggregate reports stop
CrUX and Search Console can identify a population-level problem without telling you which component or deployment caused it. Your own instrumentation can connect slow interactions to the relevant application context, subject to privacy and browser support.
This diagnostic module uses the web-vitals attribution build in a project with a compatible installed package. It logs phase durations instead of sending interaction details to a server.
import { onINP } from 'web-vitals/attribution';
onINP(({ value, attribution }) => {
console.table({
inp: value,
inputDelay: attribution.inputDelay,
processingDuration: attribution.processingDuration,
presentationDelay: attribution.presentationDelay
});
});Check the attribution API against your installed version. A production collector needs aggregation, deduplication and a policy for what context it records. Avoid collecting typed text, account identifiers or full URLs containing private query values simply because they are convenient to attach.
Treat attribution as a lead to reproduce. Coverage can differ from CrUX, and an aggregate metric can conceal several kinds of slow interaction. Group by meaningful template or component where appropriate rather than assuming that every slow sample has one cause.
Remove work that blocks the input from starting
The main thread cannot run an input handler while it is occupied by another task. A task longer than 50 milliseconds is classified as a long task, but even shorter tasks can contribute to delays when scheduling and rendering work accumulate.
Inspect the interval immediately before the interaction handlers. Common suspects include startup code, repeated timers, analytics activity, hydration and a third-party widget. The interaction target may be innocent: clicking a lightweight menu can be slow because unrelated work has already monopolized the thread.
Remove unused features and duplicate initialization before attempting elaborate scheduling. Code splitting can avoid loading and evaluating features that a particular route does not need. Deferring a script changes when it executes, but does not remove its execution cost.
For expensive code that remains necessary, inspect whether it can run in smaller tasks or outside the main thread. Our JavaScript execution guide covers those choices in more detail. The important point is to reduce contention around real interactions, not only around the initial load event.
Yield between independent pieces of work
Splitting a function into several smaller functions does not automatically split the browser task. Calling them one after another in the same handler still keeps the thread occupied until that task finishes.
The scheduler.yield() API resumes work in a later task. Browser support varies, so feature detection and a fallback are appropriate. A timer fallback creates another task but does not provide identical continuation-priority behavior.
This complete browser example processes a local collection in illustrative chunks. The chunk size is a demonstration choice, not a universal performance budget.
<button id="process" type="button">Process example data</button>
<p id="progress" role="status">Ready</p>
<script type="module">
const button = document.querySelector('#process');
const progress = document.querySelector('#progress');
const values = Array.from({ length: 10000 }, (_, index) => index);
async function yieldToBrowser() {
if (globalThis.scheduler?.yield) {
await globalThis.scheduler.yield();
} else {
await new Promise(resolve => setTimeout(resolve, 0));
}
}
button.addEventListener('click', async () => {
button.disabled = true;
progress.textContent = 'Processing';
try {
let total = 0;
for (let start = 0; start < values.length; start += 250) {
await yieldToBrowser();
const end = Math.min(start + 250, values.length);
for (let index = start; index < end; index++) {
total += values[index];
}
}
progress.textContent = `Total: ${total}`;
} finally {
button.disabled = false;
}
});
</script>Yielding gives the browser an opportunity to perform other work; it is not a promise that a particular frame will paint at a chosen instant. Measure the real operation on representative devices and adjust the work per chunk accordingly.
Do not yield halfway through a state update that must be atomic. Other code can run before the continuation, so shared state may have changed. Preserve correctness, handle cancellation where necessary and make repeated activation safe. A faster interface that applies an action twice is not an improvement.
Use requestIdleCallback only for genuinely optional background work, with feature detection and a suitable fallback. Its callback may wait while the browser is busy, so it is not a substitute for immediate interaction feedback or required completion work. A timeout can limit waiting, but then execution is no longer restricted to an idle period.
Keep the immediate response small and useful
Review the handler for work that does not belong before visual feedback. Updating a selected state may be essential. Recomputing an unrelated report, loading a hidden widget or sending nonessential analytics may not be.
Avoid replacing real feedback with a misleading success state. If an operation can fail, show that it is in progress rather than claiming it has completed. Preserve error handling and accessibility announcements so keyboard and assistive-technology users receive equivalent information.
Workers can help with suitable computation, such as parsing or transforming large data collections. They cannot directly manipulate the page DOM, and communication has costs. Moving a tiny operation to a worker may add complexity without measurable benefit; moving the right expensive operation can free the thread for input and rendering.
Also inspect how often handlers run. Duplicate listeners, repeated framework effects and unnecessary state propagation can multiply modest work. Remove the duplication rather than hiding it behind a delay.
For repeated input such as search suggestions, debouncing can avoid processing every intermediate value. Keep the input itself responsive, choose the delay around the actual task, and cancel or ignore stale asynchronous results. Do not debounce every control indiscriminately or make a visible acknowledgment wait for an arbitrary timer.
Reduce the rendering required after the handler
Presentation delay can dominate when an interaction changes a large portion of the page. Look at style recalculation, layout and paint after the handler returns. A short JavaScript function that inserts thousands of nodes can still trigger substantial browser work.
Update the smallest relevant region. Consider pagination or carefully implemented virtualization for large collections, preserving navigation, focus and accessible content. A smaller initial DOM is helpful only if the later interaction does not recreate the same bottleneck all at once.
Avoid repeatedly alternating layout reads and writes in a loop. A read that needs current geometry after a style change can force synchronous layout. Gather measurements together where possible, then apply changes, and inspect the trace to see whether forced layout decreased.
Do not treat a particular DOM-node count as a universal pass threshold. Complexity, style dependencies and the area being updated affect the cost. The evidence should be the rendering work caused by the interaction, not simply whether a counter is below a memorized number.
Audit third parties during the same action
Tag managers, chat widgets, experimentation code and advertising scripts can run around interactions even when their network requests finished earlier. A network-only audit will miss that execution cost.
In a controlled test, temporarily block a suspected resource or disable a specific integration and repeat the interaction. Check whether the page still functions and whether the change isolates the suspected cause. A missing dependency that breaks the feature can produce a misleadingly quick trace.
Give each integration an owner and a reason to run at that point in the journey. Load an optional embed on demand where that serves the user, but remember that first interaction can then include initialization. A facade should not merely move all the cost onto the click that activates it.
Validate responsiveness beyond one favorable trace
A standard Lighthouse navigation run measures loading and reports TBT as a lab indicator of blocking. It does not generate the visitor interactions needed for field INP. Do not convert TBT into INP or describe an improved Lighthouse score as proof that the slow filter is fixed.
Repeat the actual interaction under comparable conditions. Check its phases, the visible response, the final outcome and any error states. Then monitor field results by relevant device category and template after deployment.
Use the performance checklist to record the change and verification conditions, and the Core Web Vitals guides to follow any remaining loading or stability issue. Keep the interaction scenario as a regression case so future script or layout changes do not quietly bring the delay back.