Interaction to Next Paint, or INP, is how fast your page actually feels. Not just the first click. Every tap, click, and keypress after that counts too. Since Google swapped First Input Delay for INP in March 2024, this is the number that decides whether your site feels snappy or sluggish.
I wrote this for people who run WordPress sites and need INP to drop without rebuilding everything. You will get the thresholds that matter in 2026, what INP is really measuring under the hood, how to catch it with real user data, and five fixes you can ship this week. Most of them live in your JavaScript and templates, not your hosting.
TL;DR: Key Takeaways
Good INP in 2026 is 200 ms or less at the 75th percentile, measured in the field. Above that, start here:
- Ship less JavaScript. Split by route, defer what is not critical, delete what you never use.
- Keep tasks under 50 ms. Break work with
scheduler.yield()or chunkedsetTimeout(0). Push heavy work to Web Workers. - Load third parties late. Chat, GTM, Clarity. Fire them on first interaction, not on page load.
- Stop layout thrashing. Read, then write. Never both in a loop. Keep DOM under 1,500 nodes.
- Measure where it matters. CrUX and RUM (
onINP()) tell the truth. Lab TBT just helps you reproduce it.
On most WordPress sites I audit, one template drives the bulk of poor INP URLs. Fix that template and the whole site shifts. The pattern is near the end of this guide.
What Is INP? (And Why It Replaced FID)
Interaction to Next Paint (INP), as defined by web.dev, is a Core Web Vital for responsiveness. It tracks every click, tap, and keypress during a visit, then reports the 75th percentile. That is the near-worst experience, not an average that hides the bad ones.
Google retired First Input Delay in March 2024 for a simple reason. FID only timed the first interaction, and only the wait before the browser started handling it. A page could pass FID and still feel janky five seconds later. INP looks at the full lifecycle, every time.
INP thresholds in 2026
From web.dev: What is a good INP score: good is 200 ms or less, needs improvement is 200 to 500 ms, poor is over 500 ms.
- Good: 200 ms or less
- Needs improvement: 200 to 500 ms
- Poor: over 500 ms
Those numbers come from real-user field data in the Chrome User Experience Report, at the 75th percentile, split by mobile and desktop. If mobile is 260 ms, you are in needs improvement even if desktop is green.
One nuance people miss in 2026: INP is a field metric. Lighthouse cannot give you a true INP score. Lab tools can only guess at risk, usually with Total Blocking Time. Real users decide your score.
The Three Parts of INP
Every interaction breaks into three chunks. If you know which one is slow, you know what to fix.
- Input delay. The gap between what the user did and when the main thread is free to handle it. If a 180 ms task is already running, that click just waits. This is the most common culprit.
- Processing time. The work inside your event handlers. The JS that decides what happens next. Handlers that read and write the DOM, fire analytics, and force re-renders in the same tick get expensive fast.
- Presentation delay. The time from handler end to the next paint. Style recalcs, layout, and paint, especially with a huge DOM or gnarly CSS, drag this out.

The #1 Cause of Poor INP: Long Tasks
A long task is any main-thread task over 50 ms. While it runs, the browser cannot process your click. INP spikes, and there is nothing the user can do about it except wait.
What I see most often, in order:
- Monolithic bundles. 400 to 900 KB of JS parsed on load, often from page builders and leftover modules you stopped using months ago.
- Third-party scripts. Analytics, tag managers, chat widgets, ads, A/B tools. Each adds listeners and timers you did not ask for.
- Layout thrashing. Reading
offsetHeightthen writingstyle.heightin a loop. That forces a reflow every iteration. - Heavy CSS and huge DOM. 2,500 nodes or more with complex selectors makes every interaction pay a style and layout tax.
- Chatty timers and listeners.
setIntervalpolling, scroll and resize handlers that do real work on every fire.
How to find it: open DevTools, Performance tab, hit record, click around. Look for the gray bars labeled Long task with a red triangle. Click one. The bottom-up stack tells you the function and file to fix.
How to Measure INP Correctly (Field First)
Field data first. Lab second, just to iterate faster.
- Search Console, Core Web Vitals. Free CrUX data by origin and URL group, split mobile and desktop. This is your source of truth.
- PageSpeed Insights. Same CrUX INP plus lab diagnostics for a single URL.
- CrUX Dashboard or BigQuery. For history and origin versus URL comparisons.
- Real User Monitoring. The
web-vitalslibrary’sonINP(), or Cloudflare Web Analytics, Vercel, Sentry, Datadog. You get INP by interaction type, by template, by device. That segmentation is the whole point. - Lab proxy: Total Blocking Time. Lighthouse keeps TBT under 200 ms as a rule of thumb. It is not INP. But if TBT is 600 ms, field INP will almost certainly be poor.
The 30-second RUM snippet
import {onINP} from 'web-vitals';
onINP(({value, entries}) => {
// value is INP in ms at 75th percentile for this page load's interactions
console.log('INP', value, entries.at(-1));
// send to analytics: gtag, plausible, etc.
gtag('event', 'web_vital_inp', { value: Math.round(value), metric_id: 'INP' });
});
Segment by template. Homepage, blog post, product page. On most sites one template owns the majority of poor INP. Fix that template and you fix the site.
5 Steps to Fix INP (With Code You Can Ship)
1. Ship less JavaScript
- Split by route. Only load what a template needs. In WordPress, dequeue builder widgets and scripts per template.
- Defer what is not critical.
deferkeeps execution in order and out of the way of the first paint. - Delete unused code. DevTools, Cmd+Shift+P, Coverage. Record a session. Remove what never runs.
- Load third parties late. Chat and tag managers on scroll or first click, not on load. That single change often moves the needle more than anything else.
// Lazy-load a chat widget only after first user interaction
let chatLoaded = false;
['scroll','mousemove','touchstart','click'].forEach(evt =>
window.addEventListener(evt, () => {
if(chatLoaded) return; chatLoaded = true;
const s = document.createElement('script');
s.src = 'https://cdn.example.com/chat.js'; s.defer = true;
document.head.appendChild(s);
}, {once:true, passive:true})
);
2. Break up long tasks
- Use
scheduler.yield()when you can. Chromium has had it since 2024. It yields mid-task so input gets through. - Chunk with
requestIdleCallbackor batchedsetTimeout(0)if yield is not available. - Push heavy non-DOM work to Web Workers, like CSV parsing or image work.
// Before: one 260 ms task
function processBigArray(arr){ for(let i=0;i<arr.length;i++) heavy(arr[i]); }
// After: chunked with yield, never blocks more than about 40ms
async function processBigArrayYielding(arr){
const CHUNK = 80;
for(let i=0; i<arr.length; i+=CHUNK){
for(let j=i; j<Math.min(i+CHUNK, arr.length); j++) heavy(arr[j]);
if(navigator.scheduling?.isInputPending?.()) await scheduler.yield();
else await new Promise(r => setTimeout(r, 0));
}
}
3. Keep handlers tiny
- Do the visual update right away. Defer the rest to
requestAnimationFrameorsetTimeout. - Use event delegation. One listener on the parent beats 200 on individual items.
- Debounce or throttle rapid events, input, scroll, resize. Do not recompute on every keystroke.
// Delegation
document.querySelector('#product-grid').addEventListener('click', e => {
const btn = e.target.closest('[data-add-to-cart]');
if(!btn) return;
// minimal work now, rest next frame
btn.classList.add('loading');
requestAnimationFrame(() => addToCart(btn.dataset.addToCart));
});
4. Stop layout thrashing
- Batch reads, then batch writes. Never mix them in a loop.
- Use
content-visibility: autoandcontain: layouton long feeds or cards to limit render scope. - Keep the DOM lean. Under 1,500 nodes is ideal, under 2,000 is fine. Page builders love to double that with empty wrappers.
// Thrashing
items.forEach(el => { el.style.height = el.offsetHeight + 10 + 'px'; }); // read and write each loop, reflow x N
// Fixed: read then write
const heights = items.map(el => el.offsetHeight);
items.forEach((el,i) => el.style.height = heights[i] + 10 + 'px');
5. Make the browser do less
- Animate only
transformandopacity. Those stay on the compositor and avoid layout. - Avoid expensive selectors like
.a .b .c *:not(...)and large@importchains. - Preconnect to origins you must use:
<link rel="preconnect" href="https://cdn.example.com">. One line, often a free 100 ms.
WordPress and Bricks: Where INP Actually Breaks
On WordPress, INP is almost always JavaScript plus third parties plus builder wrappers. For Bricks and WARP sites, and our WordPress performance guide (2026) covers the full stack, the fixes are specific:
- Audit per template. Use Query Monitor and Coverage to find what each template actually needs. Dequeue
swiperorlightboxwhere you do not use them. - Delay third parties in WARP. In WARP Performance, turn on Delay JS and Defer Third-Party on Interaction under WARP → Performance → JavaScript. Check the docs if you have not used it before.
- Dequeue by template. In your child theme,
wp_dequeue_scriptfor sliders or maps on templates that never render them. - Fonts and icons. Two families max,
font-display: swap, subset what you ship. Icon fonts that pull 200 KB of JS are an easy delete. - Bricks DOM hygiene. Use sections and containers, not divs inside divs inside divs. Remove empty wrappers. Keep post listings under 24 items with pagination.
Illustrative Pattern: How One Template Often Drives Site-Wide INP
Illustrative pattern, not a controlled benchmark. On WordPress and Bricks builds, poor INP tends to cluster in a single template, usually the post template. The same three things show up together:
- A large shared bundle loaded everywhere
- Third-party scripts firing on load, GTM, chat, analytics
- A scroll or input handler doing layout work on every event
What tends to help when that is the case:
- Split the bundle so each template loads only what it needs
- Defer non-critical third parties until first interaction. If you use WARP Delay JS, test every interactive element after you turn it on.
- Break long tasks with
scheduler.yield()or chunkedsetTimeout, and delegate your handlers - Batch DOM reads then writes and keep the DOM lean
Prove it with field data. Check CrUX or your RUM, split by template, before and after. Lab numbers like TBT are just for reproducing locally.
Why INP Matters: Page Experience, Not Just a Score
INP sits inside Google’s page experience system. It will not save a page with weak relevance. But when two pages are close on relevance, the one that feels instant has an edge. Google says as much without publishing a neat tiebreaker formula, and that is fine. You should not chase the number. Fix the feel.
- Close calls tilt on experience. Core Web Vitals, INP included, are part of page experience. Good vitals will not outrank great content, but they can decide a close race. No public tiebreaker rule, just cumulative signal.
- Responsiveness keeps people. Laggy pages lose people. People who stay read more, scroll further, and bounce less. That behavior shows up in the data over time.
- Mobile is where it breaks. Google indexes mobile. Mobile CPUs are slower and the main thread has less room. If INP is bad, it is almost always a mobile problem first.
- Rendering has a budget. If the main thread is blocked for seconds, content and internal links appear late. Crawlers on a timeout may see a thinner version of the page.
Does the same work help AI Search and answer engines?
There is no published rule that says better INP equals more citations in AI Overviews or assistants. The overlap is practical, not causal. Pages that are faster and cleaner tend to be easier for crawlers and extractors to handle:
- Render budget is finite. Google’s renderer and many crawlers will wait only so long. If your answer needs seconds of JavaScript before it appears in the DOM, it risks being summarized incompletely.
- Good content plus good experience travels. Original, well-structured answers hold up everywhere, classic search and AI search alike. Speed alone does not earn a citation.
- Extractability overlaps. A direct answer in the first 100 words, clear H2 and H3, a tight TL;DR, FAQ with FAQPage schema, and no render-blocking bloat helps both humans and machines. Same techniques, no guarantee.
Checklist: Ship These, Then Measure in the Field
- INP at or under 200 ms at p75 on mobile and desktop, in field data
- No main-thread tasks over 50 ms. TBT under 200 ms in the lab
- JavaScript split by route, deferred, actually tree-shaken
- Third parties gated behind first interaction
- Heavy work in workers or yielded chunks
- Handlers delegated, debounced, tiny
- DOM reads and writes batched, DOM under 1,500 nodes,
content-visibilitywhere it makes sense - Animations on
transformandopacityonly - RUM running with
onINP(), split by template
Frequently Asked Questions
What is a good INP score in 2026?
Good is 200 ms or less at the 75th percentile of real interactions, measured in the field. 200 to 500 ms needs work. Over 500 ms is poor.
How is INP different from FID?
FID timed only the first interaction and only the wait before the browser reacted. INP measures all interactions, wait plus work plus paint, at the 75th percentile. It reflects the whole session, not just the entry.
Can I measure INP in Lighthouse?
Not directly. INP needs field data. Use TBT under 200 ms in Lighthouse as a rough proxy while you fix things, then confirm in CrUX or RUM.
Does INP affect SEO?
Yes, as part of page experience and Core Web Vitals. It will not outrank relevance, but in a close comparison a page with good vitals can have an edge.
What causes a high INP?
Long tasks over 50 ms that block input, plus heavy handlers and layout thrashing. On WordPress, third-party scripts are usually the trigger.
How do I improve INP quickly?
Reduce and defer JS, gate third parties behind interaction, break long tasks with scheduler.yield(), and batch DOM reads and writes. One template fix often helps the whole site.
Does INP matter for AI Overviews?
Fast, clean pages render more completely and are easier to extract answers from. That helps everywhere, but there is no documented rule that INP alone earns AI citations.
Sources & Further Reading
- Google Search Central: Introducing INP to Core Web Vitals (FID retirement)
- web.dev: Interaction to Next Paint (INP): definition and thresholds
- web.dev: How to optimize INP: yielding, workers, handlers
- MDN:
scheduler.yield()and Web Workers API - Chrome UX Report: CrUX docs and PageSpeed Insights for field data
- web-vitals library: GoogleChrome/web-vitals on GitHub (
onINP()for RUM) - WARP: How to Optimize a WordPress Site for Maximum Performance (2026), Features, Docs
Thresholds and techniques checked against web.dev and MDN as of September 2026.
Optimizing INP is not about chasing a metric. It is about making the page feel instant when someone tries to use it.
The work is consistent: less blocking JavaScript, smaller bundles, handlers that do almost nothing, a lean DOM. I would start with one bad template. Most sites only need one.
- Search Console, Core Web Vitals, filter mobile. Find your worst template.
- Record one interaction in Performance. Find the longest task and break it with
scheduler.yield(). - In WARP, set GTM and chat to Load on User Interaction. That is often the biggest single win.
Measure in the field, fix the main thread, keep RUM running. The part users feel is the part that matters. Try WARP Performance if you want that toggle without hand-rolling it: 14-day free trial.
Last updated: September 5, 2026 • Reviewed by WARP Performance engineering. Tested on Bricks, WordPress 7.1, Chromium 128.
