How to Fix Core Web Vitals in React: Lowering INP and LCP Below 200ms

Actionable guide to passing Google's Core Web Vitals in React. Learn how to diagnose INP bottlenecks, eliminate blocking JS, and reach 95+ scores.

Published by Aafaque Nazir on 2026-10-02 • 6 min read
Quick Answer: To fix Interaction to Next Paint (INP) in React, break long main-thread tasks over 50ms using React 19 startTransition or requestAnimationFrame, decouple expensive UI updates with atomic state (Zustand), and avoid layout thrashing during animation renders. For Largest Contentful Paint (LCP), preload hero images with fetchpriority='high' and eliminate render-blocking CSS/fonts.

The Cost of Poor Core Web Vitals on Organic Traffic

Google explicitly uses Core Web Vitals as a search ranking factor. When potential customers visit your website and tap a button or open a navigation drawer, even a 300ms delay sends negative behavioral signals that damage both your conversion rate and your search visibility.

While building [Restaurant OS](/projects/4), our team had to ensure that waiters and chefs handling real-time order states experienced instant, 60fps tactile feedback even on budget mobile devices. Here is the exact performance playbook we used to achieve sub-100ms INP.

1. Taming Interaction to Next Paint (INP)

The most common culprit behind a red INP score is performing heavy state computations synchronously inside an event handler. Use React's `startTransition` API to prioritize immediate user visual feedback while deferring heavy background filtering or re-calculations.

import { useState, useTransition } from "react";

export function ProductCatalogSearch({ items, onSelect }) {
  const [searchTerm, setSearchTerm] = useState("");
  const [filteredItems, setFilteredItems] = useState(items);
  const [isPending, startTransition] = useTransition();

  const handleSearchChange = (e) => {
    const query = e.target.value;
    // 1. Immediate visual update for the input field
    setSearchTerm(query);

    // 2. Non-blocking deferred transition for 1000+ items
    startTransition(() => {
      const results = items.filter((item) =>
        item.name.toLowerCase().includes(query.toLowerCase())
      );
      setFilteredItems(results);
    });
  };

  return (
    <div>
      <input
        type="text"
        value={searchTerm}
        onChange={handleSearchChange}
        placeholder="Search inventory..."
        className="px-4 py-2 bg-zinc-900 border border-white/10 rounded-xl"
      />
      {isPending && <span className="text-xs text-cyan-400">Filtering...</span>}
      {/* Product List */}
    </div>
  );
}

2. Optimizing Largest Contentful Paint (LCP)

LCP measures how long it takes for the largest visual element on the screen (usually a hero image or title banner) to become fully visible. To optimize LCP below 1.5s:

  • Preload critical hero images in the <head> using <link rel='preload' as='image' fetchpriority='high' />
  • Convert legacy PNGs and JPEGs into compressed WebP format (typically saves 70% file size)
  • Never lazy-load the hero image above the fold
  • Inline critical CSS and host fonts locally with font-display: swap
Client Benchmark Result
By applying these strict image preloading and bundle splitting techniques, my portfolio and client websites consistently achieve a 98-100/100 Lighthouse performance rating with LCP under 1.2s.

Ready to Speed Up Your Web App?

Is your website feeling sluggish or losing customers due to slow load times? [Get in touch with Aafaque Nazir](/contact) for a comprehensive frontend performance audit and speed optimization.

Frequently Asked Questions

What is Google's INP metric and why does it affect rankings?

Interaction to Next Paint (INP) measures a page's overall responsiveness to user interactions (clicks, taps, keystrokes) across its entire lifecycle. A score under 200 milliseconds is classified as 'Good'. Sites failing INP receive reduced ranking priority in Google's mobile search results.

Why do React SPAs often fail INP?

React single page applications frequently execute heavy JavaScript on user interactions—such as re-evaluating large component trees, complex JSON transformations, or triggering non-composited CSS layout calculations on the main UI thread.

How do you guarantee a 95+ Lighthouse score for clients?

In all client projects by Aafaque Nazir, assets are converted to modern WebP/AVIF formats, CSS is strictly purged with Tailwind CSS, third-party scripts are deferred, and heavy interactive components are code-split using React.lazy with Framer Motion DomAnimation.

← Back to all articles