Why Interaction to Next Paint Changed How Developers Think About Responsiveness
Source: Dev.to
Introduction
For years, First Input Delay (FID) was the responsiveness metric in Core Web Vitals. It measured one thing: the delay between a user’s first interaction and the browser starting to process it. Most sites passed FID easily because it only captured input delay on the first click, ignoring everything after that. A page could freeze for two seconds when you opened a dropdown menu, and FID would not register it.
In March 2024, Google replaced FID with Interaction to Next Paint (INP). The shift was not cosmetic. INP fundamentally changed what “responsive” means for web applications by measuring every interaction across the entire page visit—not just the first one—and by tracking the complete lifecycle of each interaction through input delay, processing, and paint.
This article explains why the change matters, what INP actually measures, and how to think about responsiveness now that the rules have changed.
Why FID fell short
- Only the first interaction was measured. If your page loaded cleanly but became sluggish after JavaScript hydration, after a state update triggered a heavy re‑render, or after a third‑party script loaded, FID would still report a good score. Users experiencing a slow, janky interface would not be reflected in the metric.
- Only input delay was counted. FID ignored how long the event handler itself took to run and how long the browser took to update the screen after the handler finished. A click with zero input delay but 800 ms of processing and paint would score perfectly under FID.
- Over‑optimistic pass rate – Chrome’s analysis of real‑user data showed that > 90 % of websites passed the 100 ms FID threshold, making it essentially useless as a differentiator, while users still reported feeling that sites were slow and unresponsive.
Photo by cottonbro studio on Pexels
Interaction to Next Paint (INP)
INP captures the full lifecycle of an interaction in three phases:
| Phase | Description |
|---|---|
| Input delay | Time between the user’s action (click, tap, keypress) and the browser starting to run your event handler. This is what FID measured, but INP captures it for every interaction, not just the first one. |
| Processing time | How long your event handler takes to execute. If clicking a button triggers a state update that causes React to re‑render a large component tree, that entire rendering time counts as processing time. |
| Presentation delay | Time between your handler finishing and the browser updating pixels on screen. This includes layout calculation, paint, and compositing. Changing CSS properties that trigger layout (e.g., width, height, position) can make this delay significant. |
The INP value for a page visit is typically the interaction with the worst latency (with statistical adjustment for pages that have many interactions).
Google’s thresholds
| INP score | Meaning |
|---|---|
| ≤ 200 ms | Good |
| 200 – 500 ms | Needs improvement |
| > 500 ms | Poor |
The web.dev INP documentation explains the statistical methodology. For most sites, the reported INP value is the 98th‑percentile interaction, meaning it reflects the worst experience a typical user encounters during their visit.
From “fast to load” to “fast to use”
Under FID
- Responsiveness was mostly about avoiding long tasks during the initial page load.
- If JavaScript didn’t block the main thread before the first click, you passed.
- Optimizations focused on code‑splitting, lazy loading, and deferring non‑critical scripts—essentially “fast to load” tactics.
Under INP
- Responsiveness must be maintained throughout the entire session.
- The focus shifts to “fast to use.”
Patterns that passed FID but fail INP
- Heavy state updates on interaction – Clicking a filter that re‑renders 500 list items blocks the main thread. Under FID this was invisible unless it happened to be the first interaction; under INP it counts every time.
- Synchronous layout calculations in event handlers – Reading layout properties (
offsetHeight,getBoundingClientRect()) inside a click handler forces a forced layout, blocking the main thread. See Google’s documentation on forced layout for details. - Third‑party scripts that run on interaction – Analytics scripts that fire on every click, chat widgets that initialize on first interaction, and consent‑management platforms that intercept touches all contribute to INP.
“INP changed the conversation from ‘how fast does the page load’ to ‘how fast does the page respond to every single thing the user does.’ That is a much harder bar to clear, and it is exactly what users actually care about.” – Dennis Traina, 137Foundry
Practical INP Optimizations
1. Break up long event handlers
If a click handler triggers work that takes > 50 ms, split it. Use scheduler.yield() (available in Chrome and behind a flag in other browsers) or setTimeout(0) to yield back to the browser between chunks of work. This lets the browser process pending user input and paint updates between your work chunks.
async function handleFilterClick(selectedFilter) {
// Update the UI immediately
showLoadingIndicator();
// Yield to let the browser paint the loading state
await scheduler.yield();
// Now do the expensive filtering
const filtered = applyFilter(data, selectedFilter);
// Yield again before rendering results
await scheduler.yield();
renderResults(filtered);
}2. Debounce rapid interactions
Scroll handlers, resize listeners, and search‑as‑you‑type inputs can fire dozens of events per second. Debounce them so the expensive work runs once after the user stops interacting, rather than on every individual event.
function debounce(fn, wait) {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => fn.apply(this, args), wait);
};
}
// Example: debounce a search input
const handleSearch = debounce((query) => {
fetchResults(query).then(renderResults);
}, 300);3. Move computation to Web Workers
Data processing, sorting, or any CPU‑intensive work that does not need direct DOM access should be off‑loaded to a Web Worker. This keeps the main thread free for user interactions and painting.
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ type: 'sort', payload: largeArray });
worker.onmessage = (event) => {
const sorted = event.data;
renderSortedList(sorted);
};// worker.js
self.onmessage = (event) => {
if (event.data.type === 'sort') {
const sorted = event.data.payload.sort((a, b) => a - b);
self.postMessage(sorted);
}
};4. Reduce forced synchronous layouts
- Avoid reading layout properties (
offsetHeight,clientWidth, etc.) after mutating the DOM in the same task. - Batch DOM reads and writes separately, or use the
requestAnimationFramepattern.
// Bad: read after write (forces layout)
button.addEventListener('click', () => {
button.style.width = '200px';
const height = button.offsetHeight; // forced layout
console.log(height);
});
// Good: separate read/write
button.addEventListener('click', () => {
requestAnimationFrame(() => {
button.style.width = '200px';
});
requestAnimationFrame(() => {
const height = button.offsetHeight;
console.log(height);
});
});5. Audit third‑party scripts
- Load analytics, chat widgets, and consent managers on demand (e.g., after user interaction).
- Use
async/deferattributes or dynamicimport()to prevent them from blocking the main thread during interactions.
Measuring and Monitoring INP
- Chrome DevTools – Open the Performance panel, record a user flow, and look for the Interaction to Next Paint section.
- Web Vitals JavaScript library – Add
web-vitalsto your site to capture INP in real‑time:
import { getINP } from 'web-vitals';
getINP((metric) => {
console.log('INP:', metric.value);
// Send to your analytics endpoint
});- Search Console – Google Search Console now reports INP under the Core Web Vitals report, showing the distribution of good / needs‑improvement / poor pages.
Conclusion
INP replaces FID to give a holistic view of responsiveness across the entire user journey. By measuring every interaction’s input delay, processing time, and presentation delay, it surfaces performance problems that previously slipped under the radar.
The good news is that the same performance‑engineering principles—short tasks, async work, avoiding forced layouts, and careful third‑party management—still apply, only now they must be applied continuously throughout the page lifecycle.
Start by auditing your longest‑running interactions, break up heavy handlers, and leverage Web Workers and debouncing. With these patterns in place, you’ll move your site from “needs improvement” to “good” on the INP metric—and, more importantly, deliver a smoother, more responsive experience for real users.
Web Workers
Large arrays and parsing JSON payloads do not need to happen on the main thread. A Web Worker runs JavaScript in a background thread that cannot block user interactions.
Optimizing React Re‑renders
React.memo– prevents unnecessary re‑renders of functional components.useMemo– memoizes expensive calculations so they run only when their dependencies change.useTransition– marks non‑urgent state updates as low priority, keeping the UI responsive during heavy updates.
The React documentation on Transitions explains how
startTransitionkeeps the UI responsive during heavy updates.
Core Web Vitals Overview
For a broader look at all three Core Web Vitals and the diagnostic workflow—from field data to fixes—see the complete guide to Core Web Vitals, which covers LCP, CLS, and INP.
Photo by Daniil Komov on Pexels
Real‑User Monitoring (RUM)
Lab tools like Lighthouse simulate interactions, but they cannot replicate the variety of real user behavior. Some users click rapidly, some use keyboard navigation, and some interact with elements that your test script never touches.
Real‑user monitoring is the only reliable way to track INP.
INP Monitoring with the web‑vitals Library
- The
web‑vitalslibrary reports INP values from actual visitors. - Integrate it with your analytics pipeline to see which pages and which interactions have the highest INP values.
- The attribution build identifies the exact element and event type responsible for the worst interaction.
Teams at 137Foundry typically pair web‑vitals monitoring with the Chrome DevTools → Interactions track to diagnose specific slow interactions identified in field data. That combination of real‑user detection and lab investigation is the fastest path to fixing INP problems.
INP Standards
INP raised the bar for what counts as a responsive web application.
- FID measured a single moment, making it relatively easy to pass.
- INP requires that every interaction, on every page, throughout the entire visit, responds within 200 ms.
This higher standard provides a more honest reflection of user experience. Sites that invest in INP optimization are genuinely faster to use, which translates to:
- Better engagement
- Lower bounce rates
- A ranking advantage in competitive search results