
INP Optimization 2026 — The Complete Guide to Sub-200ms
As of March 18, 2026 Google Search Central confirmed INP is weighted equal to LCP and CLS as a ranking signal. About 43% of sites still fail it at the 200-millisecond threshold. Most of them are running fixes from 2019 against a metric that did not exist until 2024 and did not become a full signal until this year. This post is the playbook I run on every audit, from the seven root causes to the five copy-paste patterns that fix them.
What INP actually measures
INP stands for Interaction to Next Paint. It measures the full interaction lifecycle from the user’s tap, click or keystroke through to the next visual update painted by the browser. There are three stages:
- Input delay — how long after the user interacts before the event handler runs. This is what FID used to measure.
- Processing time — how long the handler actually takes to do its work.
- Presentation delay — how long after processing before the browser paints the next frame.
Add them up. Take the 98th percentile across all interactions on the page if there are more than 50 interactions, or the maximum if there are fewer. That is the page’s INP score. Google then evaluates the 75th percentile of pages in CrUX for the origin or URL group over a rolling 28-day window. To pass, 75% of real-user sessions need to hit under 200 milliseconds on their worst interaction.
The thresholds in 2026:
| INP | Rating |
|---|---|
| ≤ 200ms | Good |
| 200ms – 500ms | Needs Improvement |
| > 500ms | Poor |
If you want the broader CWV context with LCP and CLS, the Core Web Vitals 2026 guide covers all three metrics in one place. The platform-specific Shopify playbook is on Shopify Core Web Vitals — The 2026 Playbook.
Why FID lied to you for 5 years
FID, First Input Delay, only measured the first stage: the time from input to handler start. On most sites this is microseconds because the main thread is usually idle by the time a user gets around to clicking. FID values were near-zero on roughly 85% of pages even when the page felt sluggish, which made FID a bad proxy for what users actually felt.
INP captures all three stages, which is why INP fail rates are 43% in 2026 while FID fail rates were under 5% in 2023. The metric did not change reality. The metric started measuring reality.
The 7 root causes of bad INP
⚡ 2-minute scorecard · instant result
How strong is your lead engine?
Answer 5 quick questions. Get your score + the top fixes — free.
1. Do you track which source every lead comes from?
2. Do you respond to new leads in under 5 minutes?
3. Do you have a CRM that catches every inquiry?
4. Do you run a follow-up / nurture sequence?
5. Is your site built to convert, not just inform?
1. Long JavaScript tasks blocking the main thread
Anything over 50 milliseconds of continuous JS execution is a long task. The main thread cannot paint or respond to input while a long task runs. Cart updates that recalculate the entire cart object in one synchronous loop, search filters that re-sort 500 products in one pass, analytics handlers that build a 30-key event payload — all classic long-task offenders.
2. Third-party scripts firing on page load
Chat widgets (Tidio, Intercom, Drift), review apps (Yotpo, Judge.me, Stamped), email-capture popups (Klaviyo, Privy, Justuno), analytics (GA4, Meta Pixel, TikTok Pixel), social embeds, ads, A/B test tools (VWO, Convert) — every single one of these usually injects 80 to 400 KB of JavaScript that parses and executes on page load and then sits in memory waiting to handle events. They are 40 to 70% of main-thread blocking time on a typical commerce page.
3. Large DOM size forcing expensive layout
Above about 1,500 DOM nodes, every layout calculation gets expensive. Pages with 100+ product cards rendered server-side, multi-level mega menus inflated to 200 nodes, accordion FAQs with 50 expanded panels — all push the DOM size past the threshold where the browser starts struggling on every interaction.
4. Synchronous event handlers that do too much work
The handler that calls fetch, then awaits, then loops over the response, then mutates 8 DOM elements, then logs analytics, all on the same task — that is one giant blocking operation from the browser’s perspective. Even if each step is small, the sum is a long task.
5. Layout thrash inside the handler
Reading an element’s offsetWidth, then setting its style.width, then reading offsetHeight, then setting style.height, then reading another offsetWidth. Each read forces a layout calculation because the previous write invalidated the layout. This pattern can add 50 to 200 milliseconds to a single handler.
6. JavaScript-driven animations fighting the main thread
setInterval-based animations, jQuery .animate(), GSAP without will-change hints, anything that mutates top/left/width/height instead of transform/opacity. JS animations run on the main thread and steal cycles from event handling. CSS transforms run on the GPU compositor and do not.
7. Hydration cost on framework pages
React, Vue and Svelte pages that ship in server-rendered HTML still need to hydrate before becoming interactive. On a 200-component page that can mean 800 milliseconds to 3 seconds of main-thread work after first paint, during which any user interaction queues up behind hydration. Astro and Qwik solve this with islands and resumability. Next.js 14+ partially solves it with React Server Components. Old Next.js pages, classic Nuxt, and most custom React apps still suffer.
The 5 copy-paste patterns that fix it
Pattern 1: scheduler.yield() to break long tasks
// Modern: yield control to main thread mid-task
async function processLargeArray(items) {
for (const item of items) {
doWork(item);
if ('scheduler' in window && 'yield' in scheduler) {
await scheduler.yield();
} else {
// Fallback for Safari, Firefox, older Chrome
await new Promise(resolve => setTimeout(resolve, 0));
}
}
}scheduler.yield() is in Chrome 129+ and shipping to Edge. Safari and Firefox use the setTimeout(0) fallback. Use it any time you loop over more than 50 items or any task that has been running for more than 50ms.
Pattern 2: optimistic UI in <50ms
button.addEventListener('click', async (e) => {
// 1. Paint the UI change IMMEDIATELY
button.classList.add('is-loading');
cartCount.textContent = currentCount + 1;
// 2. Yield so the browser can paint
await new Promise(r => requestAnimationFrame(r));
// 3. THEN do the expensive work
const result = await fetch('/cart/add.js', { method: 'POST', body });
// 4. Reconcile if the API disagrees
if (!result.ok) cartCount.textContent = currentCount;
});The visual change paints inside 50 milliseconds. INP scores the time to the next paint, not the time to the data settling, so this passes even if the fetch takes 800 milliseconds.
Pattern 3: Web Worker for heavy computation
// main.js
const worker = new Worker('/parser.js');
worker.postMessage({ csv: largeString });
worker.onmessage = (e) => renderResults(e.data);
// parser.js — runs off main thread
self.onmessage = (e) => {
const parsed = expensiveParse(e.data.csv);
self.postMessage(parsed);
};Any heavy computation (CSV parsing, image manipulation, complex sorting) that does not need DOM access belongs in a Web Worker. The main thread stays free for paints and event handling. INP is unaffected by what runs in the worker.
Pattern 4: debounce input handlers
function debounce(fn, ms = 150) {
let t;
return (...args) => {
clearTimeout(t);
t = setTimeout(() => fn(...args), ms);
};
}
searchInput.addEventListener('input', debounce((e) => {
runSearch(e.target.value);
}, 150));Type-as-you-search inputs fire an event on every keystroke. Without debounce, a 20-character query triggers 20 search runs, each one a long task. With a 150-millisecond debounce, only the last keystroke triggers the search. INP collapses on the search interaction.
Pattern 5: defer third-party scripts until user interaction
<!-- Don't do this -->
<script src="https://widget.tidio.co/abc.js" async></script>
<!-- Do this -->
<script>
['mousemove', 'touchstart', 'scroll', 'keydown'].forEach(evt => {
window.addEventListener(evt, loadChat, { once: true, passive: true });
});
function loadChat() {
const s = document.createElement('script');
s.src = 'https://widget.tidio.co/abc.js';
s.async = true;
document.body.appendChild(s);
}
</script>The chat widget loads only after the user has shown intent to interact. Page load is clean, INP is clean, and the widget still loads in time for the user to use it. Apply this pattern to every third-party script that does not need to run before first paint.
Stack-by-stack INP playbook
This is what I actually run when I audit a client site. The order matters because the high-impact fixes come first.
Shopify — the worst INP offender by stack
- Audit and remove unused apps. Every Shopify app that uses script_tag injects synchronous JS into the storefront. The audit list is: every app, what it does, whether it has been used in the last 30 days. Remove what has not been used. Most stores cut 30 to 50% of their app stack on the first audit.
- Defer chat, review and email-capture widgets. Move Tidio, Klaviyo, Judge.me, Yotpo and similar widgets to GTM with click/scroll triggers, or to the listener-on-first-interaction pattern above.
- Switch to a modern theme. Dawn, Sense, Craft and other Online Store 2.0 themes ship lean Liquid and minimal JS. Vintage themes from pre-2021 ship 200 to 600 KB of unused JS.
- Preload the LCP image, lazy-load the rest. Add
<link rel="preload" as="image" href="..." fetchpriority="high">for the hero image in theme.liquid head. Setloading="lazy"on everything below the fold. Always specify width and height to kill CLS. - Move third-party scripts to GTM with triggers. Fire analytics and pixels on click or scroll, not page load.
- Section-level code splitting. Only load JS for sections that actually render on the page. Most themes load everything.
- Replace JS animations with CSS transforms. translate3d() is GPU-accelerated and does not block the main thread.
- Optimistic UI for cart and filter. Show the visual response in under 50 milliseconds, then settle the data asynchronously.
- Use Shopify’s native search and filter instead of Searchanise or Boost where possible.
- Disable unused theme settings. Many themes load JS for features (mega menu, predictive search) even when the feature is turned off in admin.
- Server-side render product recommendations. Avoid client-side “you may also like” widgets that hydrate on interaction.
WordPress — the LCP and TTFB offender
- WP Rocket “Delay JavaScript Execution”. Defers chat, analytics, ads, embeds until first user interaction. Single biggest INP win on WordPress.
- Perfmatters or Asset CleanUp Pro. Selective script loading. Disable Contact Form 7 JS on pages without a form, disable WooCommerce JS on non-commerce pages, disable Elementor JS on non-Elementor pages.
- Move to a managed host. Kinsta, WP Engine, Rocket.net for TTFB under 200 milliseconds. Cheap shared hosting at $5 a month kills TTFB which kills LCP which compresses your INP budget.
- LiteSpeed Cache plus QUIC.cloud if on LiteSpeed hosts like Hostinger or NameHero. Free, beats WP Rocket on cached HTML.
- Cloudflare APO at $5 a month. Full-page HTML caching at the edge. TTFB drops to ~50 milliseconds globally.
- Audit page builders. Elementor and Divi are the worst for INP. WPBakery, Bricks, GenerateBlocks are 3 to 6 times faster on the same page.
- Remove jQuery dependencies where possible. WordPress 6.5+ ships modern JS by default.
- Replace Gravity or Ninja Forms with Fluent Forms. 10 times less JS for the same feature set.
- Remove emoji.js, embed.js and dashicons on the frontend.
- Self-host Google Fonts and preload with font-display: swap.
Webflow — clean baseline, easy to ruin
- Minimize IX2 interactions above the fold. Webflow Interactions 2.0 hydrates on page load.
- Put custom code in the Footer, not the Header. Avoid render-blocking.
- Defer all third-party scripts via GTM. Webflow has no plugin layer, so all the bloat lives in GTM and embeds.
- Use CSS transforms over Webflow’s height and width animations. translate and scale are GPU-accelerated; height and top trigger layout.
- Lazy-load CMS Collections images. Set images to lazy in the asset panel, define dimensions.
- Replace Lottie animations with CSS or WebP. Lottie players are heavy and run on main thread.
Headless (Next.js, Nuxt, Astro, Remix, Hydrogen)
- Use the platform’s image component (next/image, Image in Nuxt, Image in Astro). Auto width, height, lazy, format.
- Partial hydration or islands. Astro and Qwik default to zero-JS; Next.js 14+ supports React Server Components.
- Code-split routes plus dynamic imports for heavy widgets.
<Script strategy="lazyOnload">in Next.js for third-party scripts.- Edge runtime plus ISR for sub-200ms TTFB globally.
- Preload critical fonts and the LCP image in the document head.
The case study — 312ms to 124ms in 12 weeks
The work I keep coming back to in conversations with prospects is a Shopify Plus fashion brand I worked with in 2025. Starting point: LCP 4.7 seconds on mobile, INP 312 milliseconds, CLS 0.27. Failing all three. Mobile bounce rate at 64%.
The fixes, in order of impact:
- App audit, weeks 1 to 2. Cut from 23 apps to 11. Removed 4 unused, replaced 4 with native Shopify features, consolidated 4 into 2. INP dropped 90 milliseconds from this alone.
- Theme upgrade, weeks 3 to 5. Migrated from a 2019 vintage theme to a Dawn-based custom build. LCP dropped 1.4 seconds.
- Deferred 4 widgets via GTM, week 6. Klaviyo, Tidio, Yotpo and a back-in-stock app moved to first-interaction triggers. INP dropped another 60 milliseconds.
- Replaced 3 jQuery animations with CSS transforms, week 7. Hero parallax, product image hover, cart drawer animation. INP dropped 30 milliseconds.
- Image preload plus optimistic cart UI, week 8. LCP image preloaded with fetchpriority=”high”. Add-to-cart paints in 30ms before the fetch settles. CLS dropped to 0.04.
- Tuning and monitoring, weeks 9 to 12. DebugBear RUM caught two regressions when the team installed a new app. Both reverted before they hit production CrUX.
End state: LCP 1.9 seconds, INP 124 milliseconds, CLS 0.04. Passing all three. Mobile conversion rate lifted 17% over the following 90 days. Incremental revenue: $1.2 million in the next 6 months. The full breakdown lives in the Shopify CWV revenue lift case study.
If your store fails INP today and you want me to run the same audit, the website speed optimization service is the entry point. I do the audit, ship the fixes, and monitor monthly so the gains stick.
How to measure INP correctly
This is the part where most teams go wrong. They run Lighthouse, see a 95 score, and assume the site is fast. Lighthouse runs synthetic interactions in a controlled environment. CrUX measures real users on real devices on real networks. Lighthouse can score 95+ while CrUX shows your INP at 380ms on 4G Android phones.
The right measurement workflow:
- PageSpeed Insights for spot checks. It shows both lab (Lighthouse) and field (CrUX) data side by side. Look at the field data first.
- Search Console CWV report for trend tracking. It groups URLs and shows pass/fail trends week over week.
- Web Vitals Chrome extension during dev. Live overlay of INP, LCP and CLS as you interact with the page. Catches regressions before they ship.
- DebugBear or Treo.sh for ongoing RUM. Real-user monitoring catches the slow interactions you cannot reproduce locally.
- Lighthouse CI in your deploy pipeline. PR-level regression checks. Fail the build if Lighthouse drops below a threshold.
The mobile-first ranking reality
Google uses mobile CrUX as the primary CWV input. More than 64% of global web traffic is mobile (Q3 2025 data). If your mobile INP fails but your desktop passes, you are still ranked on the mobile fail. Test mobile first, then desktop. Most agencies invert this and miss the real problem. iPhone 14 Pro at 390×844 viewport and Android Pixel 6 at 412×915 are my default test devices, and I always test on a throttled 4G connection because that is what real users on cellular get.
What changed in 2026, what is coming in 2027
- March 18, 2026: Search Central confirmation that INP is weighted equal to LCP and CLS. No more grace period.
- TTFB watch list: Google has hinted TTFB may become a ranking signal in 2027. Engineering target is under 200 milliseconds. Official “Good” threshold remains 800 milliseconds.
- No new primary metric for 2026 beyond INP’s full activation. Visual stability sub-metrics within CLS are being explored, not announced.
- CrUX Dashboard (Looker Studio) deprecated end of 2025. Migrate to BigQuery CrUX dataset or third-party CrUX dashboards (Treo, DebugBear).
How I scope an INP fix engagement
The audit is $400 one-time, takes 5 business days, and includes: CrUX field-data audit on mobile and desktop, lab audit with PageSpeed Insights and WebPageTest, stack-specific deep dive on your apps or plugins, the prioritized fix list by impact-to-effort, and implementation of the top 3 to 5 fixes within scope.
The ongoing monitoring is $200 a month: daily CrUX and synthetic checks, alerting if any metric drops below “Good”, monthly report with trends, and a quarterly tune-up included. Cancel anytime. Most clients keep the monitoring because the cost of re-failing after a theme update or app install is enormous and the cost of catching it inside 24 hours is small.
Want me to audit your INP this week?
$400 audit, 5 business days, real CrUX data + lab analysis + the fix list ranked by impact. I implement the top 3 to 5 fixes inside the audit fee. Monitoring is $200/mo to keep it that way.
FAQ
What is INP in plain English?
INP stands for Interaction to Next Paint. It measures how long the worst (98th percentile) interaction on the page takes from the moment a user taps, clicks or types until the browser paints the next visual update. Good is 200ms or under. Poor is over 500ms. Unlike its predecessor FID, which only measured the input delay before processing started, INP measures the full interaction lifecycle: input delay, event handler processing time, and presentation delay. It is the most honest responsiveness metric Google has ever shipped.
Why did INP replace FID?
FID only measured the delay between user input and the start of event processing. It missed the work the handler actually did, and it missed the time it took to paint the result. FID values were near-zero on most sites even when the page felt sluggish, which made FID a bad proxy for real user experience. Google replaced FID with INP on March 12, 2024, and the two-year grace period ended in 2026. INP now measures all three stages of an interaction, which is what users actually feel.
Is INP a Google ranking signal?
Yes, as of March 18, 2026 Search Central confirmed INP is weighted equal to LCP and CLS as a ranking signal. Before that it was technically a Core Web Vital but the grace period meant ranking effects were soft. In 2026 it is a full ranking signal evaluated at the 75th percentile of real Chrome user data over a rolling 28-day window. Sites passing INP see roughly 10% higher CWV pass rates at position 1 vs position 9.
What is the INP good threshold in 2026?
Good is 200 milliseconds or below. Needs Improvement is 200 to 500 milliseconds. Poor is above 500 milliseconds. The threshold is measured at the 98th percentile of all interactions on a page if there are more than 50 interactions, and at the maximum interaction otherwise. Google evaluates the 75th percentile of pages in CrUX for the origin or URL group. To pass, 75% of real-user sessions need to hit under 200 milliseconds on their worst interaction.
How do I measure INP on my site?
Three free tools. PageSpeed Insights gives you single-URL field data from CrUX plus lab-data from Lighthouse. Google Search Console’s Core Web Vitals report gives you URL-group pass/fail with trend tracking. The Web Vitals Chrome extension shows you live INP, LCP and CLS overlaid on any page while you interact with it. Lighthouse alone is not enough because it runs synthetic interactions, not real ones. Validate every fix with field data, not lab scores.
What are the 7 root causes of bad INP?
Long JavaScript tasks blocking the main thread, third-party scripts firing on page load, large DOM size forcing expensive layout, synchronous event handlers that do too much work, layout thrash inside the handler (read-then-write-then-read against DOM properties), JavaScript-driven animations that fight the main thread, and hydration cost on framework pages where every interactive element rehydrates before becoming responsive. Knock each of these out and INP collapses.
What single fix moves INP the most?
Deferring third-party scripts until first user interaction. Chat widgets, review apps, email-capture popups, analytics, ads, social embeds. They are usually 40 to 70% of main-thread blocking time on a typical commerce page. On WordPress this is WP Rocket Delay JavaScript Execution. On Shopify it is moving the scripts into GTM with click/scroll triggers. On any stack it is the listener-on-first-interaction pattern. Single biggest INP win on every audit I have run.
Does Shopify have worse INP than WordPress?
On the median, no. CrUX data for Q1 2026 puts Shopify median INP at 153 milliseconds and WordPress at 220 to 320 milliseconds. Shopify’s hosted CDN and sandboxed app architecture beat the average WordPress install. But Shopify stores with 15+ apps regularly cross 300 milliseconds because every app injects synchronous JavaScript via the script_tag API. A clean Shopify store wins. A bloated Shopify store loses to a tuned WordPress install.
What is scheduler.yield and when should I use it?
scheduler.yield() is a browser API that lets you voluntarily give up the main thread mid-task so the browser can paint pending updates and respond to input. It is in Chrome 129+ and shipping to Edge. Safari and Firefox do not have it yet, so you fall back to setTimeout with 0ms. Use it any time you loop over more than 50 items or run a task longer than 50ms inside an event handler. Wrap the inside of the loop in await scheduler.yield() and INP usually drops by 60 to 90% on the affected interaction.
How does optimistic UI help INP?
Optimistic UI means painting the visual response within 50 milliseconds of user input, before the network or expensive work actually completes. The user sees the cart counter increment immediately, then the fetch to /cart/add.js runs in the background, then you reconcile if the server disagrees. INP measures the time to the next paint, not the time to the data settling, so as long as you paint fast you pass. Real perceived responsiveness improves on the same fix because users feel the page is alive.
What is the case study INP result you cite?
On a Shopify Plus fashion brand I worked with, LCP went from 4.7 seconds to 1.9 seconds, INP went from 312 milliseconds to 124 milliseconds, and CLS went from 0.27 to 0.04 over 12 weeks. Mobile conversion rate lifted 17%, which translated to $1.2 million in incremental revenue over the following 6 months. The fixes were the app audit (cut from 23 to 11), deferring 4 widgets via GTM, theme upgrade from a 2019 vintage theme to Dawn-based, and replacing 3 jQuery animations with CSS transforms.
Does removing apps actually fix INP on Shopify?
Yes, more than any other Shopify fix. Every active app that uses script_tag injects synchronous JavaScript into the storefront, regardless of whether the app is doing anything visible on the page. I have seen Shopify stores remove 8 to 15 unused apps and watch INP drop 100 to 180 milliseconds within the next CrUX window. The audit is: list every app, write down what it actually does, remove anything that has not been used in 30 days. Most stores discover 30 to 50% of their app stack is dead weight.
How long until Google notices my INP improvements?
Google uses a rolling 28-day window of CrUX data. So a fix that ships today starts showing in CrUX a few days later and is fully reflected in your 28-day average about a month after deploy. Search Console’s CWV report updates weekly. Ranking effects, if any, follow the CrUX update, so expect to wait 28 to 60 days to see organic lift from an INP fix. The conversion lift is usually visible inside 7 to 14 days because users feel the page immediately.
What tools should I use for ongoing INP monitoring?
Free tier: PageSpeed Insights for spot checks plus Search Console’s CWV report plus the Web Vitals Chrome extension during dev. Paid tier I recommend for any store over $500K GMV: DebugBear at $80 to $400 a month for synthetic plus RUM. Treo.sh for lightweight CrUX dashboards. Cloudflare Web Analytics for real-user CWV data alongside CrUX. The critical rule: Lighthouse can score 95+ while CrUX fails. Always validate with field data.
Frequently asked questions
What is INP in plain English?
Why did INP replace FID?
Is INP a Google ranking signal?
What is the INP good threshold in 2026?
How do I measure INP on my site?
What are the 7 root causes of bad INP?
What single fix moves INP the most?
Does Shopify have worse INP than WordPress?
What is scheduler.yield and when should I use it?
How does optimistic UI help INP?
What is the case study INP result you cite?
Does removing apps actually fix INP on Shopify?
How long until Google notices my INP improvements?
What tools should I use for ongoing INP monitoring?
Want me to do this for you?
Book a free 30-min strategy call. I’ll review your site live and ship 3 specific fixes you can use this week. No pitch.


