How to Fix Interaction to Next Paint (INP): A Practical Optimization Guide for 2026

· 5 min read
# How to Fix Interaction to Next Paint (INP): A Practical Optimization Guide for 2026 Interaction to Next Paint replaced First Input Delay as a Core Web Vital in March 2024, and by now it has become one of the most consequential ranking signals in Google search. Yet many site owners still treat INP as an afterthought, or worse, confuse it with older speed metrics that measured something entirely different. If your site has INP problems, you are likely losing rankings. This guide walks through exactly how to diagnose INP issues, understand what causes them, and fix the most common problems without rewriting your entire frontend. ## What INP Actually Measures INP tracks the time between a user interaction (click, tap, or keypress) and the moment the browser paints the visual result of that interaction on screen. Unlike FID, which only measured the delay before the browser started processing the first input, INP captures the full lifecycle: input delay, processing time, and presentation delay. Google evaluates INP at the 75th percentile across all page visits. That means 75% of your real users need to experience interactions that complete in under 200 milliseconds for a "good" score. Between 200ms and 500ms is rated "needs improvement," and anything above 500ms is considered poor. The practical difference from FID is significant. FID only cared about whether the main thread was available when the user first interacted. INP cares about every interaction throughout the entire page session and measures the full round trip from tap to paint. A page could pass FID easily while failing INP badly. ## Why Your INP Score Is Probably Worse Than You Think Most developers test their sites on fast hardware with stable network connections. INP scores in the field are almost always worse than what you see in lab testing, because real users interact with pages on mid-range phones while JavaScript bundles are still parsing and third-party scripts are fighting for main thread time. Check your actual field data in Google Search Console under the Core Web Vitals report, or use the Chrome User Experience Report (CrUX) data available through PageSpeed Insights. If you only rely on Lighthouse scores, you are missing the picture entirely. Lighthouse runs synthetic tests that do not capture real interaction patterns. ## Step 1: Identify Which Interactions Are Slow Before you can fix INP, you need to know which specific interactions are causing problems. There are several ways to do this. **Chrome DevTools Performance Panel.** Open DevTools, go to the Performance tab, and record a session where you interact with the page naturally. Click buttons, open menus, fill forms, toggle elements. After stopping the recording, look for long tasks (shown as red-flagged blocks on the main thread timeline) that coincide with your interactions. The gap between the interaction event and the next paint is your INP for that interaction. **The Web Vitals JavaScript Library.** Google provides the web-vitals library that you can add to your site to capture INP data from real users. The attribution build gives you detailed breakdowns of each slow interaction, including which element was interacted with and where the time was spent. ```javascript import {onINP} from "web-vitals/attribution"; onINP(function(metric) { if (metric.value > 200) { console.log("Slow interaction:", { value: metric.value, element: metric.attribution.interactionTarget, inputDelay: metric.attribution.inputDelay, processingDuration: metric.attribution.processingDuration, presentationDelay: metric.attribution.presentationDelay }); } }); ``` This tells you exactly where the bottleneck is: input delay means the main thread was busy when the user interacted, processing duration means your event handler code is slow, and presentation delay means the browser is struggling to render the visual update. ## Step 2: Reduce Input Delay Input delay happens when the main thread is occupied with other work at the moment a user interacts with the page. The user clicks a button, but the browser cannot start processing that click because it is busy executing JavaScript from a third-party analytics script, parsing a large bundle, or running a timer callback. The fixes for input delay focus on keeping the main thread available: **Break up long tasks.** Any JavaScript task that runs longer than 50 milliseconds blocks the main thread and risks delaying user input. Use `scheduler.yield()` (available in modern browsers) or `setTimeout` to break expensive work into smaller chunks that give the browser a chance to process pending interactions between chunks. ```javascript async function processLargeDataSet(items) { for (let i = 0; i < items.length; i++) { processItem(items[i]); if (i % 100 === 0) { await scheduler.yield(); } } } ``` **Defer non-critical JavaScript.** Scripts that do not need to run immediately should use `defer` or `async` attributes, or load dynamically after the page becomes interactive. Common offenders include chat widgets, analytics platforms, social media embeds, and A/B testing frameworks. **Audit third-party scripts ruthlessly.** Every third-party script you load competes for main thread time. Run a coverage analysis in DevTools to see how much of each script actually executes during a typical page session. Remove anything that is not delivering measurable value. ## Step 3: Speed Up Event Handler Processing If the processing duration is the bottleneck, your event handler code itself is taking too long to execute. This is common in JavaScript-heavy applications where a single click triggers cascading state updates, DOM manipulations, and re-renders. **Avoid layout thrashing.** Reading layout properties (like `offsetHeight` or `getBoundingClientRect`) and then immediately writing to the DOM forces the browser to recalculate layout synchronously. Batch your reads and writes separately. **Debounce expensive handlers.** For interactions that fire rapidly (scroll, resize, input events), debounce or throttle the handler so it does not run on every single event. But be careful with click handlers. Debouncing a button click feels broken to users. Only debounce continuous interactions. **Move heavy computation off the main thread.** If an interaction triggers data processing, filtering, sorting, or complex calculations, consider moving that work to a Web Worker. The main thread stays responsive while the worker handles the computation, and you update the UI when results come back. ```javascript const worker = new Worker("/data-worker.js"); button.addEventListener("click", function() { // Show immediate visual feedback button.textContent = "Processing..."; button.disabled = true; // Offload heavy work worker.postMessage({action: "filter", data: largeDataset}); }); worker.onmessage = function(event) { renderResults(event.data); button.textContent = "Filter"; button.disabled = false; }; ``` ## Step 4: Minimize Presentation Delay Presentation delay is the time between your event handler finishing and the browser actually painting the updated pixels on screen. High presentation delay usually means the browser is doing a lot of rendering work: recalculating styles, laying out elements, painting layers, and compositing. **Reduce DOM size.** Pages with thousands of DOM nodes take longer to re-render after any change. If your page has more than 1,400 nodes (Google's recommended threshold), look for opportunities to simplify the structure. Virtualize long lists so only visible items exist in the DOM. **Use CSS containment.** The `contain` property tells the browser that changes inside an element will not affect layout outside of it. This lets the browser skip recalculating large portions of the page when something changes inside a contained element. ```css .card-container { contain: layout style; } ``` **Avoid forced style recalculations.** Changing CSS classes that affect many elements simultaneously causes expensive style recalculations. When possible, scope visual changes to the smallest possible subtree of the DOM. **Use `content-visibility: auto` for off-screen content.** This CSS property tells the browser to skip rendering content that is not currently visible in the viewport, which reduces the work needed during paint updates. ## Framework-Specific Considerations If you are using React, Vue, or another component framework, INP problems often stem from unnecessary re-renders. A single state change can cascade through your component tree and trigger re-renders in components that did not actually need to update. **React:** Use `React.memo`, `useMemo`, and `useCallback` to prevent unnecessary re-renders. Use the React DevTools Profiler to identify components that re-render on interactions where they should not. Consider `useTransition` for state updates that do not need to block the UI. **Vue:** Use `v-once` for static content, `computed` properties instead of methods for derived data, and `shallowRef` or `shallowReactive` when deep reactivity is not needed. **For any framework:** Avoid updating global state on every interaction if that state is consumed by many components. Localize state changes to the component that needs them. ## Monitoring INP Over Time Fixing INP is not a one-time project. New features, updated dependencies, and additional third-party scripts can regress your scores at any time. Set up continuous monitoring using one of these approaches: - **CrUX Dashboard:** Free, uses real Chrome user data, updates monthly. Good for tracking trends but too slow for catching regressions quickly. - **Real User Monitoring (RUM):** Services like SpeedCurve, Calibre, or a custom solution using the web-vitals library give you daily or hourly INP data from your actual users. - **Performance budgets in CI:** Tools like Lighthouse CI can catch some INP regressions before they reach production, though lab data will not catch everything. The most effective approach combines RUM data for real-world visibility with CI checks for catching obvious regressions before deployment. ## Priority Checklist If you need a quick action plan, work through these items in order: 1. Check your field INP data in Search Console or PageSpeed Insights 2. Install the web-vitals attribution library to identify specific slow interactions 3. Audit and remove unnecessary third-party scripts 4. Break long JavaScript tasks into smaller chunks using scheduler.yield() 5. Move heavy computation to Web Workers where possible 6. Reduce DOM size and apply CSS containment 7. Set up ongoing RUM monitoring to catch future regressions INP optimization is not glamorous work, but it directly impacts how Google evaluates your site and, more importantly, how your users experience it. A site that responds instantly to every tap and click builds trust. A site that hesitates, even for half a second, sends users looking elsewhere. Start with the data, fix the biggest bottlenecks first, and monitor continuously. That is the entire strategy.

Ready to audit your site?

Run a free SEO scan and get actionable recommendations in seconds.

Start Free Scan →