Core Web Vitals 10 min read

What Is INP in Core Web Vitals? (And How to Improve It)

INP (Interaction to Next Paint) measures how responsive your page feels. Learn what causes poor INP, how to diagnose it, and the fixes that actually work.

By Rankture Team
What Is INP in Core Web Vitals? (And How to Improve It)

INP (Interaction to Next Paint) replaced FID as the responsiveness metric in Core Web Vitals in March 2024. If your site felt fast but now shows poor INP scores, you’re not alone—INP is a harder bar to pass.

This guide explains what INP measures, why it matters, and how to fix it.

What Is INP?

INP (Interaction to Next Paint) measures how long it takes for your page to respond to user interactions—clicks, taps, and key presses.

Specifically, INP tracks:

  1. The time from when a user interacts
  2. To when the browser can paint the visual response

Unlike the old FID metric (which only measured the first interaction), INP considers all interactions throughout the page session and reports the worst one (at the 98th percentile).

INP Thresholds

RatingINP Score
Good≤200ms
Needs Improvement200ms–500ms
Poor>500ms

To pass Core Web Vitals, 75% of your page views need an INP of 200ms or less.

Why INP Matters

It’s a Google Ranking Factor

INP is part of Core Web Vitals, which are confirmed ranking signals. Pages with poor INP may be disadvantaged in search results, especially when competing against similar pages with better scores.

It Reflects Real User Experience

A page might load fast (good LCP) but feel sluggish when you try to interact with it. INP captures that “the page isn’t responding” frustration that makes users bounce.

Common symptoms of poor INP:

How to Measure INP

Field Data (What Google Uses)

PageSpeed Insights:

  1. Go to PageSpeed Insights
  2. Enter your URL
  3. Look at the “Field Data” section
  4. Check the INP metric

Search Console:

  1. Go to Core Web Vitals report
  2. Check which URLs have INP issues
  3. Note if it’s mobile, desktop, or both

Lab Data (For Debugging)

Chrome DevTools:

  1. Open DevTools (F12)
  2. Go to Performance tab
  3. Check “Web Vitals” checkbox
  4. Record while interacting with the page
  5. Look for “Interaction to Next Paint” in the timeline

Lighthouse:

What Causes Poor INP?

INP problems almost always come down to JavaScript blocking the main thread. The browser can only do one thing at a time on the main thread, and if JavaScript is running, it can’t respond to user input.

Cause 1: Long Tasks

A “long task” is any JavaScript execution that takes more than 50ms. During that time, the browser can’t respond to clicks or key presses.

Common sources:

Cause 2: Third-Party Scripts

Third-party scripts are the #1 cause of INP problems:

These scripts often run heavy operations and don’t prioritize your page’s responsiveness.

Cause 3: Event Handler Complexity

When a user clicks a button, the event handler runs on the main thread. If that handler does too much work, INP suffers.

// Bad: Does too much in the click handler
button.addEventListener('click', () => {
  // This blocks the main thread
  const result = heavyCalculation();
  updateUI(result);
  sendAnalytics();
  updateLocalStorage();
  // User sees no response until ALL of this finishes
});

Cause 4: Layout Thrashing

Reading and writing to the DOM in rapid succession forces the browser to recalculate layout repeatedly:

// Bad: Layout thrashing
elements.forEach(el => {
  const height = el.offsetHeight; // Read (forces layout)
  el.style.height = height + 10 + 'px'; // Write (invalidates layout)
  // Next iteration reads again, forcing another layout
});

How to Fix Poor INP

Fix 1: Break Up Long Tasks

Split heavy operations into smaller chunks that yield to the main thread:

// Before: One long blocking task
function processItems(items) {
  items.forEach(item => heavyProcessing(item));
}

// After: Yielding to main thread
async function processItems(items) {
  for (let i = 0; i < items.length; i++) {
    heavyProcessing(items[i]);
    
    // Yield every 5 items
    if (i % 5 === 0) {
      await new Promise(resolve => setTimeout(resolve, 0));
    }
  }
}

Modern approach using scheduler.yield() (when available):

async function processItems(items) {
  for (const item of items) {
    heavyProcessing(item);
    
    // Yield to browser
    if ('scheduler' in window && 'yield' in scheduler) {
      await scheduler.yield();
    }
  }
}

Fix 2: Defer Non-Critical JavaScript

Move non-essential scripts out of the critical path:

<!-- Before: Blocks rendering -->
<script src="analytics.js"></script>

<!-- After: Loads after page is interactive -->
<script src="analytics.js" defer></script>

<!-- Even better: Load on user interaction -->
<script>
document.addEventListener('mouseover', () => {
  const script = document.createElement('script');
  script.src = 'analytics.js';
  document.body.appendChild(script);
}, { once: true });
</script>

Fix 3: Audit and Reduce Third-Party Scripts

For each third-party script, ask:

  1. Is it essential? Remove anything you don’t actively use
  2. Can it load later? Delay loading until after page is interactive
  3. Is there a lighter alternative? Some tools have “lite” versions
  4. Can it run in a web worker? Move processing off the main thread

Quick wins:

Fix 4: Optimize Event Handlers

Keep event handlers lean—do the minimum needed to show visual feedback:

// Good: Immediate visual feedback, defer heavy work
button.addEventListener('click', async () => {
  // 1. Show immediate feedback
  button.classList.add('loading');
  button.disabled = true;
  
  // 2. Yield to let the browser paint
  await new Promise(r => requestAnimationFrame(r));
  
  // 3. Now do the heavy work
  const result = await heavyCalculation();
  updateUI(result);
  
  // 4. Clean up
  button.classList.remove('loading');
  button.disabled = false;
});

Fix 5: Use Web Workers for Heavy Computation

Move CPU-intensive work off the main thread entirely:

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

button.addEventListener('click', () => {
  // Show loading state immediately
  showLoading();
  
  // Offload heavy work
  worker.postMessage({ data: largeDataset });
});

worker.onmessage = (e) => {
  // Update UI with results
  updateUI(e.data.result);
  hideLoading();
};
// heavy-work.js (Web Worker)
self.onmessage = (e) => {
  const result = heavyCalculation(e.data);
  self.postMessage({ result });
};

Fix 6: Avoid Layout Thrashing

Batch your DOM reads and writes:

// Bad: Read-write-read-write
elements.forEach(el => {
  const h = el.offsetHeight;
  el.style.height = h + 10 + 'px';
});

// Good: Read all, then write all
const heights = elements.map(el => el.offsetHeight);
elements.forEach((el, i) => {
  el.style.height = heights[i] + 10 + 'px';
});

Or use requestAnimationFrame to batch writes:

function updateElement(el, newHeight) {
  requestAnimationFrame(() => {
    el.style.height = newHeight + 'px';
  });
}

INP Debugging Workflow

  1. Identify the problem page in Search Console or PageSpeed Insights
  2. Open Chrome DevTools → Performance tab
  3. Enable “Web Vitals” checkbox
  4. Record while interacting with the page (click buttons, type in forms)
  5. Look for long tasks (red triangles) near interactions
  6. Expand the task to see what’s running
  7. Identify the script causing the blockage
  8. Apply the appropriate fix from above

Common Framework-Specific Issues

React

Next.js

Vue

WordPress

Frequently Asked Questions

Is INP replacing FID?

Yes. INP officially replaced FID as the responsiveness metric in Core Web Vitals in March 2024. INP is more comprehensive because it measures all interactions, not just the first one.

Does INP affect SEO?

Yes, INP is part of Core Web Vitals, which are Google ranking factors. Poor INP can disadvantage your pages in search results, especially when competing against pages with better scores.

Why is my INP score different in lab vs field data?

Lab tests (Lighthouse) measure TBT, which correlates with INP but isn’t the same metric. Field data (real users) shows actual INP from Chrome users visiting your site. They can differ because:

My site has good FID but poor INP. Why?

FID only measured the first interaction. INP measures all interactions and reports the worst one. Your page might respond quickly to the first click but slow down on subsequent interactions (common with JavaScript-heavy sites).

Next Steps

Run an audit to identify your specific INP issues:

Tags:

inp core web vitals performance javascript page speed

Share this article:

Ready to improve your SEO?

Get a free SEO audit and see exactly what needs fixing on your site

Start Free Audit