Interaction to Next Paint (INP) is the Core Web Vital that measures how fast your website responds to user interactions. It replaced First Input Delay (FID) in March 2024 as Google's responsiveness metric.
What is INP?
INP measures the time from when a user interacts with your page (click, tap, or keypress) to when the browser paints the next visual update. It captures the full interaction lifecycle:
User clicks button
ā Event handlers run (JavaScript)
ā Browser recalculates styles
ā Browser performs layout
ā Browser paints the update
= INP measurementINP thresholds:
| Score | Rating | User Experience |
|---|---|---|
| ⤠200ms | Good ā | Feels instant |
| 200ā500ms | Needs Improvement ā ļø | Noticeable delay |
| > 500ms | Poor ā | Feels broken |
INP vs FID: What Changed?
| Aspect | FID (Old) | INP (Current) |
|---|---|---|
| What it measures | Input delay only | Full interaction to paint |
| Which interactions | First interaction only | All interactions |
| Score used | Single value | 75th percentile of all |
| Captures long tasks | Partially | Fully |
| Real user impact | Low correlation | High correlation |
FID only measured the delay before your event handler ran ā it ignored how long the handler took and how long the browser took to paint. INP captures the entire pipeline.
Master this topic with hands-on labs
Go beyond reading ā build real projects in sandboxed environments with expert video guidance.
Browse Courses āHow to Measure INP
Chrome DevTools
- Open DevTools ā Performance tab
- Enable "Web Vitals" lane
- Interact with the page
- Look for INP markers in the timeline
PageSpeed Insights
Visit pagespeed.web.dev and enter your URL. The "Core Web Vitals Assessment" section shows real-user INP data from the Chrome User Experience Report (CrUX).
JavaScript API
// Using web-vitals library
import { onINP } from 'web-vitals';
onINP((metric) => {
console.log('INP:', metric.value, 'ms');
console.log('Element:', metric.attribution.interactionTarget);
console.log('Type:', metric.attribution.interactionType);
// Send to analytics
sendToAnalytics({
name: 'INP',
value: metric.value,
target: metric.attribution.interactionTarget,
});
});Lighthouse
npx lighthouse https://your-site.com --only-categories=performanceNote: Lighthouse measures lab INP (simulated). Real-user data from CrUX is more reliable.
Common INP Problems and Fixes
1. Long JavaScript Tasks
Problem: Event handlers that take > 50ms block the main thread.
// Bad ā blocks main thread
button.addEventListener('click', () => {
const result = heavyComputation(data); // 300ms
updateUI(result);
});Fix: Break up long tasks with scheduler.yield() or setTimeout:
// Good ā yields to browser between chunks
button.addEventListener('click', async () => {
showLoadingState();
await scheduler.yield(); // Let browser paint loading state
const result = heavyComputation(data);
await scheduler.yield(); // Yield again before DOM update
updateUI(result);
});2. Layout Thrashing
Problem: Reading and writing DOM properties in a loop forces synchronous layout.
// Bad ā forces layout on every iteration
items.forEach(item => {
const height = item.offsetHeight; // Read ā forces layout
item.style.height = height * 2 + 'px'; // Write ā invalidates layout
});Fix: Batch reads and writes:
// Good ā read all, then write all
const heights = items.map(item => item.offsetHeight);
items.forEach((item, i) => {
item.style.height = heights[i] * 2 + 'px';
});3. Third-Party Scripts
Problem: Analytics, chat widgets, and ad scripts compete for main thread time.
Fix: Load non-critical scripts with async or defer, and use requestIdleCallback:
<!-- Defer non-critical scripts -->
<script src="analytics.js" defer></script>
<!-- Or load dynamically after interaction -->
<script>
document.addEventListener('click', () => {
import('./heavy-widget.js');
}, { once: true });
</script>4. Large DOM Size
Problem: Pages with 1,000+ DOM nodes are slow to update.
Fix: Virtualize long lists and lazy-load off-screen content:
// Use virtual scrolling for long lists
import { useVirtualizer } from '@tanstack/react-virtual';
function VirtualList({ items }) {
const virtualizer = useVirtualizer({
count: items.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 50,
});
// Only renders visible items
}5. React Re-renders
Problem: Unnecessary component re-renders during interactions.
Fix: Memoize expensive components and use useTransition:
import { useTransition, memo } from 'react';
const ExpensiveList = memo(({ items }) => (
items.map(item => <ListItem key={item.id} {...item} />)
));
function SearchPage() {
const [query, setQuery] = useState('');
const [isPending, startTransition] = useTransition();
function handleSearch(e) {
const value = e.target.value;
setQuery(value); // Urgent: update input immediately
startTransition(() => {
filterResults(value); // Non-urgent: can be deferred
});
}
}Get weekly IT automation tips
Docker, Ansible, Terraform, MLOps ā curated insights delivered to your inbox. No spam.
Subscribe Free āINP Debugging Checklist
- Identify the worst interaction ā use
web-vitalsattribution to find which element and event type - Profile in DevTools ā Performance tab ā record ā interact ā find long tasks
- Check for layout thrashing ā purple "Layout" bars in the flame chart
- Audit third-party scripts ā block them one by one to measure impact
- Measure DOM size ā
document.querySelectorAll('*').length(aim for < 1,500) - Test on real devices ā lab data on fast laptops hides real-world issues
INP and SEO
Google uses Core Web Vitals (including INP) as a ranking signal. Poor INP can:
- Lower your search rankings
- Trigger "Page experience" warnings in Search Console
- Reduce user engagement (slow sites lose visitors)
A good INP score helps both SEO and user retention.
What's Next?
Building fast, responsive web applications requires solid fundamentals. Our Node.js REST APIs course teaches you to build performant backends, and our Docker Fundamentals course covers optimized deployment. First lessons are free. -e ---
Ready to go deeper? Explore our hands-on DevOps courses ā practical labs covering Docker, Ansible, Terraform, and more.
Ready to learn by doing?
Stop reading tutorials ā start building. Expert video courses with hands-on labs in real sandboxed environments.
Related Articles
Redis for Developers Quick Start
Get started with Redis. Data types, caching patterns, pub/sub messaging, session management, rate limiting, and deployment practices.
Git for DevOps Engineers
Essential Git workflows for DevOps: branching strategies, interactive rebasing, cherry-picking, bisect debugging, and CI/CD integration.
TypeScript Express Tutorial 2026
Build a production REST API with TypeScript and Express. Complete tutorial covering project setup, routing, middleware, and deployment.
How to Install OpenClaw on Ubuntu Server
Step-by-step guide to installing and configuring OpenClaw on Ubuntu. Get your self-hosted AI agent running in under 10 minutes.
Internal Developer Platform Guide
Build an internal developer platform that developers actually use. Learn the five layers of an IDP, common mistakes, build vs buy decisions.
Istio Service Mesh Beginner Guide
Istio adds mTLS, traffic management, and observability to your Kubernetes services without code changes. Learn the core concepts, installation, and practical.
Explore topics
Browse more articles on the topics covered here.