Key Takeaways
- Interaction to Next Paint (INP) replaced First Input Delay (FID) as a Core Web Vital in March 2024 — it measures the responsiveness of all user interactions, not just the first one
- Keep INP under 200ms to pass Google's "good" threshold — this indirectly supports AI visibility through better Google rankings
- AI crawlers do not interact with pages, so INP does not directly affect crawling — but it matters for the Google ranking signals that feed AI source selection
- The main cause of poor INP is long JavaScript tasks blocking the browser's main thread — break them into smaller chunks
- Reducing DOM size, optimizing event handlers, and auditing third-party scripts are the three highest-impact fixes for INP
How do your Core Web Vitals stack up? Run a free AI visibility scan — includes performance analysis for AI crawler optimization.
Table of Contents
What Is INP and Why It Replaced FID
Interaction to Next Paint (INP) is a Core Web Vitals metric that measures how quickly your page responds to user interactions. When a user clicks a button, taps a link, or types in a form field, INP captures the time from the interaction to the moment the browser paints the visual response.
INP replaced First Input Delay (FID) as a Core Web Vital in March 2024. The reason was straightforward: FID only measured the delay of the very first interaction on a page, ignoring all subsequent interactions. A page could have excellent FID (fast first click response) but terrible responsiveness afterward — and FID would never catch it.
INP tracks every interaction throughout the page's lifecycle and reports the worst one (technically the 98th percentile to exclude statistical outliers). This gives a far more accurate picture of the user's actual experience.
For AI SEO, INP fits into the broader Core Web Vitals framework. While AI crawlers do not click or type (making INP irrelevant to the crawling process itself), INP is a Google ranking factor. Since AI models like ChatGPT and Gemini frequently draw sources from Google-ranked content, poor INP can shrink your presence in that source pool.
The connection between INP and JavaScript rendering for AI is also significant — the same JavaScript issues that cause poor INP often prevent AI crawlers from accessing client-rendered content.
INP Thresholds and AI SEO Impact
Google defines three INP ranges:
- Good: 200ms or less
- Needs Improvement: 200ms to 500ms
- Poor: More than 500ms
These thresholds apply to the worst interaction on your page (98th percentile). If 98% of interactions are under 200ms but one common interaction takes 600ms, your INP score will reflect that 600ms interaction.
How INP affects AI visibility
The relationship is indirect but meaningful:
| INP Range | Google Ranking Impact | AI Visibility Effect | |---|---|---| | Under 200ms | Positive CWV signal | Strong source pool position | | 200-500ms | Neutral to negative | Reduced competitiveness | | Over 500ms | Negative ranking factor | Weakened source pool position |
Pages that pass all three Core Web Vitals (LCP, CLS, and INP) receive a ranking boost in Google. This ranking boost influences whether your content appears in the pool of sources AI models consider when generating responses.
How INP Is Measured
INP measures three phases of every interaction:
-
Input delay — The time from when the user interacts to when the event handler starts running. This is typically caused by other JavaScript tasks occupying the main thread.
-
Processing time — The time the event handler(s) take to execute. This is the work your code does in response to the interaction.
-
Presentation delay — The time from when the event handler finishes to when the browser paints the visual result. This includes style calculations, layout, and paint operations.
INP = Input Delay + Processing Time + Presentation Delay
All three phases contribute to INP, and each requires different optimization strategies. A common mistake is focusing only on processing time while ignoring input delay (which is often the largest contributor on JavaScript-heavy pages).
Common Causes of Poor INP
Understanding what causes poor INP is the first step to fixing it:
1. Long JavaScript tasks (most common)
The browser's main thread can only do one thing at a time. When a long JavaScript task (over 50ms) is running and the user interacts with the page, the interaction must wait until the task finishes. This waiting time becomes input delay.
Common culprits: analytics initialization, framework hydration, ad script processing, and large data transformations.
2. Heavy event handlers
Event handlers that perform too much synchronous work increase processing time. Examples include: filtering large lists on every keystroke, complex calculations in click handlers, and synchronous API calls.
3. Excessive DOM size
Pages with more than 1,500 DOM elements experience slower rendering. After an event handler runs, the browser must recalculate styles, perform layout, and paint the changes. Larger DOMs make these operations slower, increasing presentation delay.
4. Layout thrashing
Reading layout properties (like offsetHeight) then immediately writing layout properties (like setting style.height) in a loop forces the browser to recalculate layout on every iteration. This dramatically increases both processing and presentation time.
5. Third-party script interference
Analytics, chat widgets, A/B testing tools, and ad networks all compete for main thread time. Each script adds to the overall main thread busyness, increasing input delay for all interactions.
Optimizing JavaScript Execution
The core principle: keep the main thread available for user interactions by breaking long tasks into smaller pieces.
Yield to the main thread
Use scheduler.yield() (where available) or setTimeout to break long tasks:
async function processLargeDataset(items) {
for (let i = 0; i < items.length; i++) {
processItem(items[i]);
// Yield every 50 items to let interactions through
if (i % 50 === 0) {
await scheduler.yield();
}
}
}
For browsers that do not support scheduler.yield(), fall back to:
function yieldToMain() {
return new Promise(resolve => setTimeout(resolve, 0));
}
Defer non-critical work
Use requestIdleCallback for tasks that do not need to happen immediately:
requestIdleCallback(() => {
// Analytics, prefetching, non-critical updates
initializeAnalytics();
prefetchNextPage();
});
Code-split aggressively
Load only the JavaScript needed for the current page. Dynamic imports allow loading additional code on demand:
button.addEventListener('click', async () => {
const { handleClick } = await import('./click-handler.js');
handleClick();
});
This reduces initial bundle size and keeps the main thread clear during page load — which is when input delay is typically worst.
Event Handler Best Practices
Efficient event handlers directly reduce INP processing time:
Keep handlers minimal
Do the minimum work needed in the handler, then defer the rest:
// Bad: heavy work in handler
input.addEventListener('input', (e) => {
const results = filterDatabase(e.target.value); // Expensive
renderResults(results); // Expensive
});
// Good: debounce and defer
input.addEventListener('input', (e) => {
clearTimeout(timer);
timer = setTimeout(() => {
const results = filterDatabase(e.target.value);
renderResults(results);
}, 150);
});
Use passive event listeners
For scroll and touch events, add { passive: true } to prevent blocking the main thread:
document.addEventListener('scroll', handleScroll, { passive: true });
Avoid forced synchronous layouts
Never read layout properties inside event handlers that also modify the DOM:
// Bad: forces synchronous layout
elements.forEach(el => {
const height = el.offsetHeight; // Read (forces layout)
el.style.height = height + 10 + 'px'; // Write (invalidates layout)
});
// Good: batch reads, then batch writes
const heights = elements.map(el => el.offsetHeight); // All reads
elements.forEach((el, i) => {
el.style.height = heights[i] + 10 + 'px'; // All writes
});
Reducing DOM Size and Rendering Work
After event handlers execute, the browser must render the visual changes. Smaller, simpler DOMs render faster:
Target DOM size
- Good: Under 800 elements
- Acceptable: 800-1,500 elements
- Problematic: Over 1,500 elements
- Critical: Over 3,000 elements
Virtualize long lists
If your page displays hundreds of items (product listings, search results, data tables), use list virtualization. Libraries like react-window or @tanstack/virtual only render items visible in the viewport:
// Only 20 visible items are in the DOM, not 1,000
<VirtualList items={allItems} itemHeight={50} visibleItems={20} />
Use CSS containment
The contain property limits which parts of the DOM the browser must recalculate when something changes:
.card {
contain: content; /* Layout changes inside don't affect outside */
}
Minimize rendering scope
When updating the DOM, target the smallest possible subtree. Instead of replacing an entire list, update only the changed item. Use requestAnimationFrame for visual updates to batch them with the browser's rendering cycle.
Testing and Monitoring INP
INP requires real interactions to measure, making field data essential:
Field data (real users):
- Web Vitals JavaScript library — Tracks every interaction and reports INP to your analytics
- CrUX (Chrome User Experience Report) — Provides 75th percentile INP from real Chrome users
- Google Search Console — Core Web Vitals report shows pages with INP issues
Lab testing (approximation):
- Chrome DevTools Performance panel — Record interactions and analyze the timeline for input delay, processing, and presentation phases
- Lighthouse TBT (Total Blocking Time) — Correlates with INP but measures something different; use as a directional indicator
- WebPageTest — Can simulate interactions and measure response time
The key distinction: Lighthouse cannot directly measure INP because it does not simulate user interactions. TBT is the closest lab metric proxy, but real-user measurement through the Web Vitals library or CrUX is the definitive data source.
Frequently Asked Questions
What is INP and how does it differ from FID?
INP measures the latency of all user interactions throughout the page lifecycle and reports the worst one (98th percentile). FID only measured the first interaction's delay. INP replaced FID as a Core Web Vital in March 2024 because it provides a more complete picture of page responsiveness.
What is a good INP score?
Google considers INP under 200ms as "good," 200-500ms as "needs improvement," and above 500ms as "poor." For AI SEO, aim for under 200ms because Core Web Vitals influence Google rankings, which feed the content pool AI models use for source selection.
Does INP directly affect AI crawlers?
No. AI crawlers do not interact with pages — they fetch and parse HTML without clicking, tapping, or typing. INP affects AI visibility indirectly through Google ranking signals. Poor INP weakens your Google rankings, which can reduce your presence in the source pool AI models reference. See our Core Web Vitals and AI SEO guide for the full picture.
What causes poor INP?
The most common causes are long-running JavaScript tasks blocking the main thread, heavy event handlers doing too much synchronous work, excessive DOM size slowing rendering updates, layout thrashing, and third-party scripts competing for main thread time.
How do I fix poor INP?
Break long JavaScript tasks into smaller chunks using scheduler.yield() or setTimeout. Minimize work in event handlers by deferring non-critical updates. Reduce DOM size and use CSS containment. Audit third-party scripts for main thread impact. For JavaScript-specific guidance, see our JavaScript rendering for AI guide.
Can I test INP in Lighthouse?
Lighthouse provides Total Blocking Time (TBT), which correlates with INP but is not the same metric. INP requires real user interactions. Use Chrome DevTools Performance panel for lab testing with simulated interactions, and the Web Vitals library or CrUX for field data.
How do AI crawlers experience your site?
Get your free AI Score in 60 seconds — see performance, technical access, and content quality all in one report.
Trusted by 2,400+ websites -- No credit card required