Technical SEO · July 3, 2026 · 7 min read
INP Optimization Playbook: How to Fix Interaction to Next Paint Before It Tanks Your Rankings
Step-by-step INP optimization guide: diagnose input delay, fix heavy event handlers, and reduce presentation delay before rankings drop.
By FluxWriter Team
INP optimization is no longer optional—Google made Interaction to Next Paint a Core Web Vital in March 2024, replacing First Input Delay, and sites that ignore it are already seeing ranking pressure. Unlike FID, which only measured input delay, INP covers the full latency of every click, tap, and key press from the moment of interaction to the next frame paint. That broader scope means most sites that passed FID are failing INP.
This playbook skips the theory and goes straight to diagnosis and fixes.
Understanding What INP Actually Measures
INP captures three phases of every interaction:
- Input delay — time between the user's action and when the browser starts processing the event handler.
- Processing time — time spent running your JavaScript event handlers.
- Presentation delay — time from when handlers finish to when the frame actually paints.
A "good" INP score is under 200 ms. "Needs improvement" is 200–500 ms. Above 500 ms is poor. Google's field data shows the 75th percentile interaction on the page sets the INP score—not the worst single interaction, but the worst within a realistic distribution.
The most common culprits by phase:
| Phase | Common Cause |
|---|---|
| Input delay | Long tasks blocking the main thread |
| Processing time | Heavy event handlers, synchronous DOM reads |
| Presentation delay | Forced layout/reflow, large render tree updates |
Step 1: Diagnose in the Field First
Lab tools like Lighthouse run in a controlled environment with no real user behavior. INP almost always shows up in field data before lab tools catch it.
Sources to check:
- Chrome UX Report (CrUX) — free, origin-level data, updated monthly. Query it via the CrUX API or BigQuery.
- PageSpeed Insights — pulls CrUX field data for your URL and flags INP issues directly.
- Search Console Core Web Vitals report — shows which URL groups are "poor" or "needs improvement" with real user segmentation.
- web-vitals JavaScript library — instrument your own site to capture INP per interaction type and send to your analytics.
import { onINP } from 'web-vitals/attribution';
onINP(({ value, attribution }) => {
const { eventType, eventTarget, inputDelay, processingDuration, presentationDelay } = attribution;
sendToAnalytics({ value, eventType, eventTarget, inputDelay, processingDuration, presentationDelay });
});
This attribution breakdown is the fastest way to identify which event type and which DOM element is causing your worst interactions. Group by eventTarget in your analytics to find the hottest offenders.
Step 2: Profile Long Tasks Causing Input Delay
Long tasks—JavaScript that runs for more than 50 ms without yielding—are the primary driver of input delay. While a long task is executing, the browser cannot process input. If a user clicks during a 300 ms task, that's 300 ms of input delay before the handler even starts.
How to find them:
- Open Chrome DevTools → Performance tab.
- Record a session while clicking the slow element.
- Look for red triangles at the top of the flame chart. These mark long tasks.
- Identify the function at the top of the call stack during the long task.
How to break them up:
Replace synchronous loops with scheduler yields:
// Before: blocks the main thread
function processItems(items) {
for (const item of items) {
expensiveOperation(item);
}
}
// After: yields between chunks
async function processItems(items) {
for (const item of items) {
expensiveOperation(item);
await scheduler.yield(); // releases control to the browser
}
}
scheduler.yield() is now supported in Chrome 115+ and Edge. For broader support, fall back to setTimeout(resolve, 0) wrapped in a Promise.
Step 3: Slim Down Event Handlers
Even after clearing input delay, heavy event handlers inflate processing time. Two patterns cause most of the damage.
Synchronous DOM reads inside handlers
Mixing reads and writes forces the browser to recalculate layout mid-handler (layout thrashing). Each forced layout can cost 5–50 ms on a real device.
// Bad: read → write → read → write = multiple forced layouts
element.addEventListener('click', () => {
const height = element.offsetHeight; // forces layout
element.style.height = height + 10 + 'px'; // invalidates layout
const newHeight = element.offsetHeight; // forces layout again
});
// Good: batch reads, then writes
element.addEventListener('click', () => {
const height = element.offsetHeight; // one read
// do all writes after
element.style.height = height + 10 + 'px';
});
Third-party scripts injecting into handlers
Tag managers, analytics, and chat widgets often listen to every click on the page via event delegation. A single third-party listener that does synchronous work on every click can add 50–200 ms to processing time without any trace in your own code. Profile with third-party scripts disabled to isolate the delta.
Step 4: Reduce Presentation Delay
The last phase—presentation delay—is where many teams stop looking. Even if your handler completes in 20 ms, a large DOM update can delay the paint by another 300 ms.
Key causes:
- Large DOM trees — Chrome's rendering pipeline slows significantly above ~1,500 nodes. Audit with
document.querySelectorAll('*').length. - CSS animations and transitions triggered synchronously — if an animation is applied immediately in the event handler, the browser may need to calculate styles for thousands of elements.
- Font blocking — if a new text style is applied and the font isn't cached, text may block rendering.
Practical fix: defer non-critical visual updates
button.addEventListener('click', () => {
// Critical: update state immediately
updateButtonState(button);
// Non-critical: defer to next frame
requestAnimationFrame(() => {
updateSidebar();
refreshCounters();
});
});
This keeps the critical feedback path fast while pushing secondary DOM work out of the initial paint.
Step 5: Target Mobile and Low-End Devices
INP scores in CrUX are heavily weighted toward mobile users on mid-range and low-end Android devices. A fix that works on a MacBook may be insufficient on a Moto G Power.
Use Chrome DevTools CPU throttling (6x slowdown) to simulate these conditions in the lab. If you're seeing INP > 200 ms at 6x throttle, your field data on real Android devices is likely worse.
For React and other virtual-DOM frameworks, a common fix is to move state updates into startTransition:
import { startTransition } from 'react';
function handleClick(value) {
// Urgent: tell React the input changed now
setInputValue(value);
// Non-urgent: re-rendering the big list can wait
startTransition(() => {
setSearchResults(filterList(value));
});
}
startTransition marks the re-render as non-urgent, letting React yield to the browser between work units and preventing the UI from blocking on a large list re-render.
Validating Fixes Before Deploying
Don't rely on a single Lighthouse run. Use this validation sequence:
- Lab test: Chrome DevTools Performance panel with 6x CPU throttle. Confirm no long tasks > 50 ms during interactions.
- Synthetic test: WebPageTest with a real Android device profile. Check the INP metric in the test results.
- Staging RUM: Deploy the web-vitals snippet to staging with interaction attribution. Generate a representative sample of clicks and verify
processingDurationandinputDelaydistributions. - Canary rollout: Deploy to 5–10% of traffic, monitor CrUX via the API for 2–4 weeks (CrUX updates monthly, but the API has a 28-day rolling window).
FAQ
Does fixing INP directly improve Google rankings?
INP is a Core Web Vital used as a ranking signal via the Page Experience system. Google has confirmed CWVs are a tiebreaker signal—not a dominant ranking factor—but on competitive SERPs where other signals are close, a "poor" INP can cost positions. More reliably, a faster INP improves engagement metrics that indirectly affect ranking.
My INP is fine on desktop but poor in Search Console. Why?
CrUX aggregates field data from Chrome users across all devices. Most traffic on most sites skews toward mobile on mid-range hardware. Desktop is typically 3–5x faster at JavaScript execution than median Android devices. Check the Search Console CWV report with the "Phone" filter applied—that's where the problem likely lives.
How long does it take for INP fixes to show up in Search Console?
The CrUX dataset used by Search Console is a 28-day rolling window. After deploying a fix, expect 4–8 weeks before the Search Console report fully reflects the improvement, because old "poor" interactions need to age out of the window. You can track progress faster with the CrUX History API, which provides monthly snapshots going back further.
Practical Takeaway
Start with CrUX field data and web-vitals attribution to find the real offenders—don't guess. Break up long tasks with scheduler.yield(), batch DOM reads before writes, defer non-critical visual updates to the next frame, and validate with CPU throttling before declaring a fix done. INP problems compound on mobile; always profile on throttled conditions.
If you're managing a content site and want INP diagnostics surfaced alongside your other technical SEO signals without setting up separate tooling, FluxWriter includes Core Web Vital tracking in its site audit workflow—so slow interactions don't slip through unnoticed.