Skip to main content
</>Rosecraft Studios
performanceweb-developmentseonext-js

Web Performance: A Practical Guide to Core Web Vitals

5 min read

Why Core Web Vitals Matter

Core Web Vitals help describe loading, responsiveness, and visual stability. They are useful signals for finding friction in a real user journey. A good lab score alone does not establish a good experience for everyone or guarantee search rankings or conversions.

This guide describes a measurement workflow. It does not claim a Lighthouse score for this site or publish unverified client benchmarks.

The Three Metrics That Matter

Largest Contentful Paint (LCP)

Good threshold: 2.5 seconds or less

LCP measures how long it takes for the largest visible element (usually a hero image or heading) to render. It's the metric users "feel" most directly.

// Bad: Unoptimized hero image blocks LCP
<img src="/images/hero.png" alt="Hero" />

// Good: Optimized with next/image, priority loading
import Image from 'next/image';

<Image
  src="/images/hero.webp"
  alt="Hero section showcasing our engineering work"
  width={1200}
  height={630}
  priority
  sizes="100vw"
/>

Key strategies:

  1. Use priority on the LCP element — Tells Next.js to preload the image
  2. Compare formats — Compare AVIF, WebP, and JPEG at an acceptable visual quality
  3. Right-size images — Use sizes prop so the browser downloads the correct variant
  4. Measure server response time — Check query time, cache behavior, and hosting latency

Use Lighthouse for repeatable lab investigations. Use CrUX, when available, or consent-aware real-user monitoring for field measurements. A synthetic browser run is not equivalent to field data.

Interaction to Next Paint (INP)

Good threshold: 200 milliseconds or less

INP replaced First Input Delay (FID) in March 2024. It measures the latency of all interactions throughout the page's lifecycle, not just the first one.

The biggest INP killers:

  1. Long JavaScript tasks — Any task over 50ms blocks the main thread
  2. Hydration work — Attaching behavior to a large client interface can compete with interactions
  3. Event handlers doing too much — Click handlers that trigger cascading state updates
// Bad: Heavy computation in click handler
function handleFilter(tag: string) {
  const filtered = allPosts
    .filter((p) => p.tags.includes(tag))
    .sort((a, b) => b.date - a.date)
    .map((p) => ({ ...p, excerpt: generateExcerpt(p.content) }));
  setFilteredPosts(filtered);
}

// Good: Memoize expensive computations, defer non-critical work
const filteredPosts = useMemo(
  () => allPosts.filter((p) => activeTag === 'all' || p.tags.includes(activeTag)),
  [allPosts, activeTag],
);

Reducing unnecessary client JavaScript can help. Also inspect long tasks, event handlers, and rendering costs in the interactions people actually use. Measure before choosing an optimization.

Cumulative Layout Shift (CLS)

Good threshold: 0.1 or less

CLS measures unexpected layout shifts, grouped into session windows; some shifts following user input are excluded. Common causes to investigate include:

  1. Images without dimensions — The browser doesn't know how much space to reserve
  2. Fonts loading late — Text reflows when custom fonts replace system fonts
  3. Dynamic content injection — Ads, embeds, or lazy-loaded content pushing elements around
// Bad: Image causes layout shift
<img src="/photo.jpg" alt="Team photo" />

// Good: Explicit dimensions prevent shift
import Image from 'next/image';

<Image
  src="/photo.jpg"
  alt="Team photo"
  width={800}
  height={600}
  className="rounded-lg"
/>

Self-hosting fonts with next/font and choosing suitable fallback metrics can reduce loading and layout problems. With display: swap, fallback text may still appear before the font loads:

import { Poppins, Inter } from 'next/font/google';

const poppins = Poppins({
  subsets: ['latin'],
  weight: ['600', '700', '800'],
  variable: '--font-heading',
  display: 'swap',
});

Our Performance Checklist

Use this checklist as a starting point, adjusting budgets to the page and its audience:

Images

  • All images use next/image with explicit width and height
  • Hero/LCP image has priority flag
  • Below-fold images use loading="lazy" (default in Next.js)
  • Format priority: AVIF, WebP, then PNG/JPEG fallback
  • Set and measure image budgets appropriate to the content and target connection

JavaScript

  • Default to Server Components (zero client JS)
  • "use client" only for interactive components
  • Code-split heavy components with dynamic() imports
  • No unused dependencies in the bundle
  • Tree-shaking verified with @next/bundle-analyzer

CSS

  • Tailwind CSS purges unused utilities at build time
  • No render-blocking external stylesheets
  • Inspect the actual stylesheet loading behavior of your production build
  • Animations use transform and opacity only (GPU-composited)

Fonts

  • Self-hosted fonts with fallback metrics checked for layout shifts
  • display: swap for progressive rendering
  • Only load weights actually used (not the full family)
  • Preload the primary heading font

Measuring in Production

Report LCP, INP, and CLS from real visits using the web-vitals library, with collection and consent appropriate to the site. Preserve metric names, values, page context, and the sample period; avoid including personal form data or secret URLs.

Evaluate the 75th percentile of visits and separate mobile from desktop. If CrUX has insufficient traffic for a page, report that limitation instead of treating an absent result as a passing score. The Web Vitals overview explains the thresholds and the difference between field and lab tools.

Reporting Results Clearly

A useful before-and-after report names the page, device profile, connection, software version, test date, sample size, and change being assessed. Keep lab scores separate from field percentiles. Include a trace or stored report so someone else can check the conclusion.

Is your site's performance holding back your search rankings? Schedule a performance audit and we'll identify exactly what's slowing you down and how to fix it.

Share this article

Corey Rosamond, Founder and Principal Engineer of Rosecraft Studios

Corey Rosamond

Founder & Principal Engineer

Learn more

Enjoyed this article?

Get notified when we publish new insights on web development and engineering.