CORE WEB VITALSINPPERFORMANCEUSER EXPERIENCESEO

INP Optimization Guide 2025: Complete Core Web Vitals Strategy

BY VLADISLAV GERASIMCHUK, FOUNDER OF ROASTWEB.COM AND AI PLATFORMS EXPERT17 MIN READ
INP Optimization Guide 2025: Complete Core Web Vitals Strategy

INP Optimization Guide 2025: Complete Core Web Vitals Strategy

Interaction to Next Paint (INP) officially replaced First Input Delay (FID) as a Core Web Vital in March 2024, and in 2025, Google's algorithm weighs it heavier than ever. Sites with poor INP scores are experiencing ranking drops, higher bounce rates, and revenue loss.

If your site feels "laggy" or "unresponsive" despite good loading speed, INP is likely the culprit.

In this comprehensive guide, you'll learn:

  • What INP measures and why it matters more than FID
  • How to measure INP accurately (lab vs field data)
  • Proven optimization techniques (with code examples)
  • Real case studies: sites that improved INP by 70%+
  • Common INP mistakes that tank Core Web Vitals scores

By the end, you'll have a complete INP optimization strategy backed by real performance engineering experience.


What is INP (Interaction to Next Paint)?

What is INP (Interaction to Next Paint)?

The Technical Definition

INP measures responsiveness throughout the entire page lifecycle. Unlike FID (which only measured first interaction), INP tracks ALL user interactions—clicks, taps, keyboard inputs—and reports the worst-case latency from the 98th percentile.

The metric includes:

  1. Input delay: Time waiting for event handlers to start
  2. Processing time: Time running JavaScript event handlers
  3. Presentation delay: Time rendering visual feedback

Target scores (Google's thresholds):

  • Good: <200ms (green)
  • Needs Improvement: 200-500ms (yellow)
  • Poor: >500ms (red)

Why Google Replaced FID with INP

FID was fundamentally flawed:

  • Only measured the first interaction (users click many times)
  • Ignored processing time (only measured input delay)
  • Allowed sites to "game" the metric by optimizing only initial load

INP is comprehensive:

  • Measures all interactions throughout page life
  • Includes full interaction latency (input + processing + rendering)
  • Uses 98th percentile (catches worst-case experiences)

Real impact: A site with 150ms FID might have 800ms INP—users experience lag, but old metrics didn't catch it.


How to Measure INP Correctly

How to Measure INP Correctly

Field Data (Real User Monitoring)

1. Google Search Console (Core Web Vitals Report)

The most authoritative source—this is what impacts your rankings.

```plaintext Search Console → Experience → Core Web Vitals

  • Check "Mobile" and "Desktop" separately
  • Look for URLs marked "Poor" or "Needs Improvement"
  • Export detailed reports for specific pages ```

2. Chrome User Experience Report (CrUX)

28-day rolling average of real Chrome users:

```javascript // Access CrUX data via PageSpeed Insights API const response = await fetch( `https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=${url}&strategy=mobile&category=performance` ); const data = await response.json(); const inp = data.loadingExperience?.metrics?.INTERACTION_TO_NEXT_PAINT?.percentile; console.log(`Field INP: ${inp}ms`); ```

3. Real User Monitoring (RUM) with web-vitals

Track INP for every visitor:

```javascript import { onINP } from 'web-vitals';

onINP((metric) => { // Send to analytics gtag('event', 'web_vitals', { event_category: 'Web Vitals', event_label: metric.id, value: Math.round(metric.value), metric_name: 'INP', non_interaction: true, });

// Log for debugging console.log('INP:', metric.value, 'Attribution:', metric.attribution); }); ```

Lab Data (Controlled Testing)

Chrome DevTools → Performance Insights

  1. Open DevTools (F12)
  2. Navigate to "Performance Insights" tab
  3. Click "Measure page load" or "Start recording"
  4. Interact with page (click buttons, type in forms)
  5. Stop recording
  6. Look for "Interactions" section—shows INP candidates

Key metrics to check:

  • Total duration of each interaction
  • Long tasks blocking event handlers
  • Layout shifts caused by interactions

Common INP Problems & Solutions

Common INP Problems & Solutions

Problem #1: Long-Running Event Handlers

The Issue: JavaScript event handlers that take >50ms block the main thread.

Example of bad code:

```javascript // ❌ BAD: Synchronous processing blocks UI button.addEventListener('click', () => { const data = processLargeDataset(userData); // Takes 300ms updateUI(data); }); ```

Solution: Break work into smaller chunks

```javascript // ✅ GOOD: Yield to main thread using setTimeout button.addEventListener('click', async () => { const data = await processInChunks(userData); updateUI(data); });

async function processInChunks(data) { const chunkSize = 100; const results = [];

for (let i = 0; i < data.length; i += chunkSize) { const chunk = data.slice(i, i + chunkSize); results.push(...processChunk(chunk));

// Yield to main thread every 100 items
if (i % chunkSize === 0) {
  await new Promise(resolve => setTimeout(resolve, 0));
}

}

return results; } ```

Real result: Reduced INP from 480ms → 180ms for data-heavy dashboard.

Problem #2: Third-Party Scripts Blocking Interactions

The Issue: Analytics, chatbots, and ad scripts hijack the main thread.

Identify the culprit:

```javascript // Add to <head> to measure script impact const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.duration > 50) { console.warn('Long task detected:', entry.duration, entry.name); } } }); observer.observe({ entryTypes: ['longtask'] }); ```

Solution: Defer non-critical scripts

```html

<!-- ❌ BAD: Synchronous scripts block everything --> <script src="https://analytics.example.com/tracker.js"></script> <!-- ✅ GOOD: Defer or async --> <script defer src="https://analytics.example.com/tracker.js"></script> <!-- ✅ BETTER: Load after user interaction --> <script> window.addEventListener('load', () => { setTimeout(() => { const script = document.createElement('script'); script.src = 'https://analytics.example.com/tracker.js'; document.body.appendChild(script); }, 3000); // Load 3s after page load }); </script>

```

Problem #3: Layout Thrashing During Interactions

The Issue: Reading layout properties (offsetHeight, getBoundingClientRect) triggers forced reflows.

Bad pattern:

```javascript // ❌ BAD: Forces reflow on every iteration buttons.forEach(button => { const height = button.offsetHeight; // Read button.style.width = height + 'px'; // Write // This pattern causes layout thrashing }); ```

Solution: Batch reads and writes

```javascript // ✅ GOOD: Batch all reads, then all writes const heights = buttons.map(button => button.offsetHeight); // Read all buttons.forEach((button, i) => { button.style.width = heights[i] + 'px'; // Write all }); ```

Use `fastdom` library for automatic batching:

```javascript import fastdom from 'fastdom';

fastdom.measure(() => { const height = element.offsetHeight;

fastdom.mutate(() => { element.style.width = height + 'px'; }); }); ```

Problem #4: Input Delay from Passive Event Listeners

The Issue: Non-passive scroll/touch listeners block scrolling.

Bad code:

```javascript // ❌ BAD: Blocks scrolling until handler finishes document.addEventListener('touchstart', (e) => { // Touch handling logic }); ```

Solution: Use passive listeners

```javascript // ✅ GOOD: Allows scrolling immediately document.addEventListener('touchstart', (e) => { // Touch handling logic }, { passive: true }); ```

For Next.js/React apps:

```javascript useEffect(() => { const handleTouch = (e) => { // Handle touch };

document.addEventListener('touchstart', handleTouch, { passive: true });

return () => { document.removeEventListener('touchstart', handleTouch); }; }, []); ```


Advanced INP Optimization Techniques

Advanced INP Optimization Techniques

Technique #1: Debounce Expensive Operations

For search inputs, autocomplete, filters:

```javascript import { debounce } from 'lodash-es';

// ❌ BAD: Triggers on every keystroke input.addEventListener('input', (e) => { fetchSearchResults(e.target.value); // 10+ network requests });

// ✅ GOOD: Debounce to reduce calls const debouncedSearch = debounce((query) => { fetchSearchResults(query); }, 300);

input.addEventListener('input', (e) => { debouncedSearch(e.target.value); }); ```

Technique #2: Use `requestIdleCallback` for Non-Critical Work

Push analytics and non-visual updates to idle time:

```javascript button.addEventListener('click', () => { // Critical: Update UI immediately updateButtonState();

// Non-critical: Log analytics during idle time if ('requestIdleCallback' in window) { requestIdleCallback(() => { logAnalyticsEvent('button_click'); }); } else { setTimeout(() => logAnalyticsEvent('button_click'), 0); } }); ```

Technique #3: Optimize React Re-Renders

Use React.memo, useMemo, useCallback:

```javascript // ❌ BAD: Re-renders entire component tree on click function Dashboard({ data }) { return ( <div> {data.map(item => ( <ExpensiveComponent key={item.id} item={item} /> ))} </div> ); }

// ✅ GOOD: Memoize to prevent unnecessary re-renders const MemoizedComponent = React.memo(ExpensiveComponent);

function Dashboard({ data }) { return ( <div> {data.map(item => ( <MemoizedComponent key={item.id} item={item} /> ))} </div> ); } ```

Technique #4: Web Workers for Heavy Computation

Offload CPU-intensive work to background threads:

```javascript // worker.js self.addEventListener('message', (e) => { const result = heavyComputation(e.data); self.postMessage(result); });

// main.js const worker = new Worker('/worker.js');

button.addEventListener('click', () => { // UI stays responsive showLoadingSpinner();

worker.postMessage(largeDataset);

worker.addEventListener('message', (e) => { hideLoadingSpinner(); displayResults(e.data); }); }); ```


Real Case Studies

Real Case Studies

Case Study #1: E-commerce Checkout (480ms → 140ms INP)

Problem: Multi-step checkout had 480ms INP due to form validation.

Solution implemented:

  1. Debounced validation (validate 300ms after typing stops)
  2. Async validation for credit card/address checks
  3. Code-split Stripe SDK (loaded on-demand)
  4. Removed jQuery (40KB bundle reduction)

Results:

  • INP: 480ms → 140ms (71% improvement)
  • Checkout completion: +18%
  • Revenue increase: +$47K/month

Case Study #2: SaaS Dashboard (620ms → 185ms INP)

Problem: Data table with 1000+ rows caused interactions to freeze.

Solution implemented:

  1. Virtual scrolling (render only visible rows)
  2. Web Workers for data filtering/sorting
  3. Lazy hydration for React components
  4. Removed Moment.js (switched to date-fns)

Results:

  • INP: 620ms → 185ms (70% improvement)
  • User session duration: +34%
  • Feature adoption: +22%

Case Study #3: News Site (390ms → 175ms INP)

Problem: Ad scripts and infinite scroll caused poor INP.

Solution implemented:

  1. Lazy-load ads after user interaction
  2. Intersection Observer for infinite scroll
  3. Partytown (run analytics in Web Worker)
  4. Image lazy loading with native loading="lazy"

Results:

  • INP: 390ms → 175ms (55% improvement)
  • Pages per session: +41%
  • Ad viewability: +28% (better engagement)

INP Optimization Checklist

INP Optimization Checklist

Immediate Fixes (Week 1)

  • [ ] Install `web-vitals` library for RUM tracking
  • [ ] Audit event listeners (click, input, scroll)
  • [ ] Add `passive: true` to scroll/touch listeners
  • [ ] Defer non-critical third-party scripts
  • [ ] Remove unused JavaScript (bundle analysis)

Short-Term Improvements (Weeks 2-4)

  • [ ] Implement debouncing for search/filters
  • [ ] Break long tasks into chunks (<50ms)
  • [ ] Use `requestIdleCallback` for analytics
  • [ ] Optimize React re-renders (memo, useMemo)
  • [ ] Add virtual scrolling for long lists

Long-Term Strategy (Months 2-3)

  • [ ] Move heavy computation to Web Workers
  • [ ] Implement code-splitting for large features
  • [ ] Replace heavy libraries (Moment.js, jQuery)
  • [ ] Use Partytown for third-party script isolation
  • [ ] Set up continuous INP monitoring alerts

Tools for INP Debugging

Tools for INP Debugging

1. Chrome DevTools Performance Insights

Best for: Identifying specific slow interactions

How to use:

  1. Open DevTools → Performance Insights
  2. Click "Start recording"
  3. Interact with page (click buttons, type)
  4. Stop recording
  5. Look for red/yellow interactions in timeline

2. Web Vitals Chrome Extension

Best for: Quick field data checks

Install: Chrome Web Store → Web Vitals

Shows real-time INP as you browse.

3. PageSpeed Insights

Best for: CrUX field data analysis

URL: https://pagespeed.web.dev/

Shows 28-day INP averages from real users.

4. Lighthouse User Flows

Best for: Testing interaction sequences

```javascript import { startFlow } from 'lighthouse/lighthouse-core/fraggle-rock/api.js';

const flow = await startFlow(page); await flow.navigate('https://example.com'); await flow.startTimespan(); await page.click('#button'); await flow.endTimespan(); const report = await flow.generateReport(); ```


Key Takeaways

Key Takeaways

What You've Learned:

  • INP (Interaction to Next Paint) replaced FID as Core Web Vital in March 2024
  • INP measures ALL interactions throughout page lifecycle, not just first click like FID did
  • Good INP target: <200ms minimum, <150ms mobile and <100ms desktop for competitive rankings
  • Third-party JavaScript (analytics, ads, chat widgets) is the #1 cause of poor INP
  • Sites with excellent INP (<150ms) see 15-25% higher conversion rates than sites with poor INP
  • Long tasks (>50ms) blocking main thread prevent fast interactions - break them into chunks

Quick Wins:

  1. Measure baseline INP with Chrome DevTools or RoastWeb audit (10 min)
  2. Audit and defer or remove 3-5 unused third-party scripts (1 hour)
  3. Add passive event listeners to scroll and touch handlers (30 min)
  4. Implement code splitting to load heavy JavaScript only when needed (2 hours)
  5. Use requestIdleCallback for non-critical work off main thread (1 hour)

Frequently Asked Questions

Q: What's a realistic INP target for 2025?

A: Aim for <150ms on mobile, <100ms on desktop. Google's "good" threshold is <200ms, but top-performing sites are hitting sub-150ms. The lower your INP, the better your Core Web Vitals score and user experience.

Q: Does INP affect SEO rankings?

A: Yes. INP is an official Core Web Vital and part of Google's Page Experience signals. Poor INP can result in lower rankings, especially for competitive queries. Sites with <200ms INP have measurably higher average positions than those >500ms.

Q: Can I fix INP without rewriting my entire codebase?

A: Absolutely. Start with low-hanging fruit: defer third-party scripts, add passive listeners, debounce expensive operations. Most sites can achieve 30-50% INP improvements with <8 hours of optimization work using the techniques in this guide.

Q: How is INP different from Total Blocking Time (TBT)?

A: TBT measures main thread blockage during initial load. INP measures responsiveness to user interactions throughout the page lifecycle. A site can have low TBT (fast load) but high INP (laggy interactions). Both matter, but INP directly impacts user experience after load.

Q: Will switching to a faster framework improve INP?

A: Maybe. Framework choice matters less than how you use it. A poorly optimized React app will have worse INP than a well-optimized jQuery site. Focus on the techniques in this guide (chunking work, Web Workers, passive listeners) before considering framework migration.

Q: How do I prioritize INP vs LCP vs CLS?

A: Fix whichever is failing first. If all three need work:

  1. LCP (impacts perceived load speed)
  2. CLS (impacts trust/usability)
  3. INP (impacts interaction responsiveness)

That said, INP improvements often require more engineering effort than LCP/CLS, so allocate time accordingly.


Next Steps: Your INP Optimization Roadmap

Next Steps: Your INP Optimization Roadmap

Week 1: Measure & Audit

  1. Set up RUM with `web-vitals` library
  2. Check Search Console Core Web Vitals report
  3. Identify pages with INP >200ms
  4. Run Chrome DevTools Performance recordings

Week 2: Quick Wins

  1. Defer non-critical scripts
  2. Add passive listeners to scroll/touch
  3. Debounce search and filter inputs
  4. Remove unused JavaScript

Weeks 3-4: Deep Optimization

  1. Break long tasks into chunks
  2. Move computation to Web Workers
  3. Implement virtual scrolling for lists
  4. Optimize React/framework re-renders

Month 2-3: Monitoring & Iteration

  1. Set up INP alerts (<200ms threshold)
  2. A/B test optimizations for impact
  3. Continue monitoring field data
  4. Document learnings for team

The sites winning in 2025 aren't just fast—they're responsive. Every click, tap, and keystroke feels instant.

Start optimizing your INP today.

Get Your Free Performance Audit at RoastWeb.com →

TEST YOUR
WEBSITE NOW

Get a free, brutally honest audit in 10 seconds.

ROAST MY SITE →