Interaction to Next Paint (INP) became a Core Web Vital in March 2024, replacing First Input Delay (FID). If you're still tracking FID, you're optimizing for a metric that no longer affects your Google search rankings. INP is both harder to optimize and more representative of real user experience — which is exactly why Google made the switch.
Why Google Replaced FID with INP
FID measured only the delay before the browser could begin processing the user's first interaction with a page. This was a useful but limited metric:
FID problems:
- Only measured the first interaction (the most important one? Not always)
- Didn't measure how long the interaction actually took to complete
- Missed all subsequent interactions in a user session
- Couldn't capture the rendering time after the handler ran
Consider a user who successfully clicks a button (good FID) but then spends the entire session struggling with laggy dropdowns, slow form inputs, and unresponsive filters. FID reported a great score; the user had a terrible experience.
INP measures the worst-case interaction throughout the entire session, giving a much more accurate picture of page responsiveness.
What INP Measures
INP = Time from user input to the next frame being painted that reflects the response to that input
User clicks/taps/keys → Input Processing → JavaScript handlers run → Layout → Paint
INP = Total time from input to paint
FID = Only the initial input delay (before handlers run)
INP captures the full cycle, including JavaScript handler execution time and the subsequent rendering work.
Interactions INP measures:
- Mouse clicks
- Touches on touchscreens
- Keyboard key presses
Interactions INP ignores:
- Hover events
- Scroll events (these are handled separately via responsiveness metrics)
- Pointer move events
How INP is calculated: INP takes all qualifying interactions during a session and reports the worst-case (highest) latency, with some outlier exclusion for very long sessions.
INP Thresholds
| Score | INP Value | User Experience | |-------|-----------|----------------| | Good | ≤ 200 milliseconds | Feels instant to users | | Needs Improvement | 200 – 500 milliseconds | Noticeable lag | | Poor | > 500 milliseconds | Clearly sluggish; frustrating |
The 200ms threshold is based on research showing users perceive interactions under 100ms as "instant" and interactions up to 200ms as "fast." Above 200ms, interactions start to feel sluggish.
Diagnosing High INP
High INP is almost always caused by long JavaScript tasks that block the main thread:
User interaction occurs
Main thread busy with long task (> 50ms)
Interaction event queued
Long task completes
Interaction handler runs
Rendering happens
Total time: Long task duration + handler time + render time = High INP
Tools for INP diagnosis:
-
Chrome DevTools Performance tab: Record while interacting with your page. Look for "Long Task" indicators (red triangles).
-
Web-vitals attribution API:
import { onINP } from 'web-vitals/attribution';
onINP(({ value, attribution }) => {
const { eventTarget, eventType, loadState, inputDelay, processingDuration, presentationDelay } = attribution;
console.log('Slow interaction:', {
element: eventTarget,
event: eventType,
inputDelay, // How long before handler started
processingDuration, // How long handler ran
presentationDelay, // Time from handler end to paint
total: value
});
});
- Profiler extension: The Web Vitals Chrome extension shows real-time INP scores and highlights slow interactions.
Common Causes of High INP
Long JavaScript Event Handlers
The most direct cause: your click handler runs expensive computation synchronously:
// BAD: Expensive synchronous computation in click handler
button.addEventListener('click', () => {
// This blocks the main thread for potentially hundreds of ms
const result = processLargeDataset(data);
updateUI(result);
});
// GOOD: Break up work, yield to browser between tasks
button.addEventListener('click', async () => {
showLoadingState();
// Yield to allow the browser to paint the loading state
await scheduler.yield();
// Run expensive work
const result = await processInChunks(data);
updateUI(result);
});
React/Framework Re-Render Cascades
In React applications, a single state change can trigger re-renders across a large component tree. If that re-render takes 200ms, every interaction that triggers it has poor INP.
// Optimization: Memoize expensive computations
const ExpensiveList = React.memo(({ items }) => {
return items.map(item => <Item key={item.id} {...item} />);
});
// Optimization: Avoid re-rendering large lists on every state change
const [filter, setFilter] = useState('');
const filteredItems = useMemo(() =>
items.filter(i => i.name.includes(filter)),
[items, filter]
);
Third-Party Script Interference
Third-party scripts (ads, analytics, chatbots) that run JavaScript on the main thread steal time from interaction handlers. A chatbot widget that runs every 500ms to check for messages directly worsens INP.
Audit third-party scripts in your DevTools Performance recording and evaluate whether their INP impact justifies their presence.
Layout Thrashing
Code that alternately reads and writes DOM layout properties forces the browser to recalculate layout repeatedly:
// BAD: Forces layout recalculation on every iteration
for (const element of elements) {
element.style.width = element.offsetWidth + 10 + 'px'; // Read then write
}
// GOOD: Batch reads, then batch writes
const widths = elements.map(el => el.offsetWidth); // All reads
elements.forEach((el, i) => el.style.width = widths[i] + 10 + 'px'); // All writes
Monitoring INP Continuously
INP is a field metric — it requires real user interactions to measure. Lab tools can simulate interactions but don't capture the full diversity of user behavior.
Field monitoring strategy:
- Implement the web-vitals library and capture INP from real users
- Segment by page, device type, and interaction type
- Alert when P75 INP exceeds 200ms for a significant page or user segment
- Track INP trends over time to catch regressions
// Send INP data with page context
onINP(({ value, attribution }) => {
sendMetric({
metric: 'inp',
value,
page: window.location.pathname,
interactionTarget: attribution.eventTarget,
interactionType: attribution.eventType,
device: getDeviceType(),
});
});
Lab monitoring for regression detection:
Even though INP is a field metric, lab tools can detect INP regressions if you script specific interactions. AzMonitor's Playwright-based performance checks can click specific buttons and measure interaction responsiveness.
Add performance monitoring to AzMonitor alongside availability monitoring for complete web performance coverage. See also Core Web Vitals monitoring for the full picture.
3 monitors free forever · No credit card needed · Set up in 2 minutes
Start monitoring free →