Technology

Web Performance: Core Web Vitals Optimization

Technical strategies to improve LCP, FID, and CLS—the metrics that determine Google rankings and user experience.

All articles
TechnologyNexaEx TeamFebruary 1, 2026 9 min read
Web Performance: Core Web Vitals Optimization

Mastering Core Web Vitals

Google's Core Web Vitals measure user experience. They directly impact search ranking and user retention. Optimizing them is non-negotiable.

The Three Vitals

Largest Contentful Paint (LCP): Time until the main content is visible and interactive. Target: <2.5 seconds.

Interaction to Next Paint (INP): Time from user input (click, tap) to visual feedback. Target: <200ms.

Cumulative Layout Shift (CLS): How much the page shifts during load. Target: <0.1.

Optimizing LCP

LCP measures when the largest visible element renders. Common culprits and fixes:

1. Server-side rendering: Render initial HTML on server, not browser.

// Bad: Client-side render
export default function Page() {
  const [posts, setPosts] = useState()
  useEffect(() => {
    fetch('/api/posts').then(r => r.json()).then(setPosts)
  }, [])
  if (!posts) return <Loading />
  return <div>{posts.map(...)}</div>
}

// Good: Server-side fetch
async function PostsPage() {
  const posts = await fetch('https://api.example.com/posts')
  return <div>{posts.map(...)}</div>
}

2. Image optimization: Large images delay LCP significantly.

  • Use WebP with JPEG fallback.
  • Serve responsive images: <img srcset="small.jpg 480w, large.jpg 1200w" sizes="..." />
  • Lazy-load below-the-fold images: <img loading="lazy" />
  • Use fetch-priority="high" on critical images.

3. Font loading: Web fonts block rendering. Use font-display: swap:

@font-face {
  font-family: 'Custom';
  src: url('custom.woff2') format('woff2');
  font-display: swap; /* Use fallback while loading */
}

Or preload critical fonts:

<link rel="preload" as="font" href="custom.woff2" crossorigin />

4. CSS and JavaScript: Minimize render-blocking resources.

  • Inline critical CSS (above-the-fold).
  • Defer non-critical JavaScript: <script defer>.
  • Use async for analytics and non-blocking third-party scripts.

Optimizing INP

INP measures responsiveness. It's slower than First Input Delay (FID) because it measures all interactions.

1. Avoid long tasks: Anything over 50ms blocks interaction.

// Bad: Blocks for >50ms
function expensiveCalculation() {
  for (let i = 0; i < 100000000; i++) {
    // Heavy computation
  }
}

// Good: Break into chunks
async function expensiveCalculation() {
  for (let i = 0; i < 10000000; i++) {
    if (i % 1000000 === 0) await new Promise(r => setTimeout(r, 0))
  }
}

2. Use debouncing and throttling:

function debounce(fn, delay) {
  let timeoutId
  return (...args) => {
    clearTimeout(timeoutId)
    timeoutId = setTimeout(() => fn(...args), delay)
  }
}

window.addEventListener('resize', debounce(handleResize, 200))

3. Optimize input handlers: Minimize work inside click/submit handlers.

// Bad: Heavy computation on input
input.addEventListener('change', () => {
  const result = expensiveCalculation(input.value)
  updateUI(result)
})

// Good: Debounce expensive work
const debouncedHandle = debounce(() => {
  const result = expensiveCalculation(input.value)
  updateUI(result)
}, 300)

input.addEventListener('change', debouncedHandle)

Optimizing CLS

CLS measures visual stability. Shifts frustrate users and cause misdirected clicks.

1. Reserve space for dynamic content:

// Bad: Ad loads and shifts content
<div>
  {/* Ad loads here, shifts everything down */}
  <div id="ad-container"></div>
  <article>Your content</article>
</div>

// Good: Reserve space
<div style={{ height: '280px' }}>
  <div id="ad-container"></div>
</div>
<article>Your content</article>

2. Animations and transitions: Use transform and opacity, not width or height.

/* Bad: Causes layout shift */
.box { width: 100px; transition: width 0.3s; }
.box:hover { width: 200px; }

/* Good: No layout shift */
.box { transform: scale(1); transition: transform 0.3s; }
.box:hover { transform: scale(2); }

3. Avoid inserting content above the fold dynamically.

Measurement Tools

  • Google PageSpeed Insights: Lab and field data, recommendations.
  • WebPageTest: Waterfall charts, video, detailed diagnostics.
  • Chrome DevTools Lighthouse: Local testing during development.
  • Web Vitals JavaScript Library: Monitor real-user metrics.
import { getLCP, getFID, getCLS } from 'web-vitals'

getCLS(console.log) // Logs CLS as it changes
getFID(console.log)
getLCP(console.log)

Real-World Impact

A SaaS company optimized LCP from 4.5s to 2.2s, INP from 300ms to 180ms, and CLS from 0.25 to 0.08. Results: 25% more conversions, 18% lower bounce rate, and ranking boost for 40+ keywords.

Frequently asked questions

What's a good LCP score?

Under 2.5 seconds is good. 2.5-4 seconds is fair. Over 4 seconds is poor. Measure on real devices and networks, not just desktop.

How do I fix cumulative layout shift?

Reserve space for dynamic content, avoid DOM insertions above the fold, and use transform/opacity instead of width/height animations.

Does server-side rendering always improve LCP?

Usually, but it depends. Server rendering helps if the server fetches data fast. If server is slow, client-side rendering can be faster.

Let's build your next idea

One conversation to scope the work, meet the team, and get a proposal — usually within two business days.