İçeriğe geç
Technical SEO

Core Web Vitals INP Optimization 2026: A/B Test Findings Across 50 Pages

·13 min min read·Emre Çelik

Key Decision (TL;DR)

INP (Interaction to Next Paint) replaced FID as the third Core Web Vitals metric in March 2024. As of 2026, the "good" threshold is ≤200 ms and "poor" is >500 ms. A 6-week A/B test across 50 pages cut our P75 INP from 312 ms to 148 ms. This report quantifies the contribution of each intervention.

  • Sample: 50 pages, 6 weeks, 1.4M interactions
  • Starting P75 INP: 312 ms (borderline)
  • Final P75 INP: 148 ms ("good" band)
  • Top intervention: Third-party script lazy-load (−87 ms)

How INP Differs From FID

FID measured only the first user interaction. INP tracks all interactions over a page's lifetime and reports the worst (P98). That's why many FID-"good" pages dropped into INP "needs improvement." INP has three components:

  1. Input delay: Time before the browser starts processing the event (usually long tasks).
  2. Processing time: Time the event handler's JavaScript takes to run.
  3. Presentation delay: Time until the next frame is painted.

Test Methodology

50 pages, split into 25 control / 25 treatment, with interventions stacked one per week. Data: CrUX API + RUM (Real User Monitoring); Lighthouse alone is insufficient because it can't simulate true interaction diversity. Each week we applied one intervention and collected 7 days of data before stacking the next.

Intervention 1 — Third-Party Script Lazy-Load (Week 1)

Gain: P75 INP −87 ms

Biggest single win. Analytics, chat widgets, A/B test SDKs were deferred until after the first user interaction using requestIdleCallback or defer/async. 62% of prior main-thread blocking came from these scripts.

// Before
<script src="https://cdn.example/chat.js"></script>

// After
<script>
if ('requestIdleCallback' in window) {
  requestIdleCallback(() => {
    const s = document.createElement('script');
    s.src = 'https://cdn.example/chat.js';
    document.head.appendChild(s);
  }, { timeout: 3000 });
}
</script>

Intervention 2 — Use yield() in Event Handlers (Week 2)

Gain: P75 INP −34 ms

Long event handlers were split with scheduler.yield() (Chrome 129+). Yielding releases the main thread for a frame paint, then resumes.

async function handleClick() {
  doFirstPart();
  await scheduler.yield();
  doSecondPart();
  await scheduler.yield();
  doThirdPart();
}

Fallback: setTimeout(fn, 0) works, but yield is more efficient — no priority loss.

Intervention 3 — React Concurrent Features (Week 3)

Gain: P75 INP −22 ms

On React 18+ pages, useTransition and useDeferredValue moved expensive renders to a lower-priority queue. Filter/search UIs stopped blocking input.

const [isPending, startTransition] = useTransition();
const handleSearch = (q) => {
  setQuery(q);
  startTransition(() => setResults(filter(q)));
};

Intervention 4 — CSS Containment & content-visibility (Week 4)

Gain: P75 INP −15 ms

Long lists used content-visibility: auto and contain: layout style paint to zero out off-screen layout/paint cost — cuts the presentation-delay component directly.

.product-card {
  content-visibility: auto;
  contain-intrinsic-size: 0 280px;
}

Intervention 5 — Image Decoding Hint (Week 5)

Gain: P75 INP −9 ms

All <img> tags got decoding="async" and loading="lazy" — except the LCP image. The browser moves decode off the main thread.

Intervention 6 — Web Worker Offload (Week 6)

Gain: P75 INP −13 ms

JSON parsing, fuzzy search, large-array work moved to a Web Worker. Freeing the main thread for interactions tightened P75 measurably.

Intervention Comparison Table

InterventionEffortP75 INP GainROI
3rd-party script lazy-load1 hour−87 msVery high
scheduler.yield()3 hours−34 msHigh
React concurrent features4 hours−22 msHigh
content-visibility2 hours−15 msMedium
Image decoding hint30 min−9 msHigh
Web Worker8 hours−13 msMedium

Monitoring Stack

  • CrUX API: Real-user P75 (28-day rolling)
  • web-vitals.js v4: In-page RUM collection
  • Lighthouse CI: Pre-deploy regression check
  • BigQuery + Looker Studio: Trend tracking

4 Common Mistakes

  1. Trusting only Lighthouse: Lab P75 doesn't match field. RUM is required for INP.
  2. Using requestAnimationFrame like yield: rAF runs at frame start; it doesn't release the main thread.
  3. Applying loading=lazy to everything: Lazy on the LCP image delays LCP by 200+ ms.
  4. Passing DOM to a Worker: Workers can't access the DOM — only send data via postMessage.

Editorial Note

INP isn't won by a single visual tweak like LCP — it's a question of JavaScript architecture discipline. The six interventions delivered −180 ms combined; the biggest contributor was also the easiest to ship: deferring third-party scripts. Practical tip: ship script lazy-load in Week 1 alone — that single change moves many sites into the "good" band.

Related: our prior post on Keyword Cannibalization Audit covers content-side wins; INP is the technical complement.

#Core Web Vitals#INP#Performance#Technical SEO#JavaScript Optimization
Back to Blog