Core Web Vitals: The Complete Guide to Page Speed in 2025
PerformanceOptimizationWeb Vitals

Core Web Vitals: The Complete Guide to Page Speed in 2025

January 12, 2025
10 min read
Salman Izhar

Optimizing Web Performance: A Practical Guide

In today's fast-paced digital world, every millisecond counts. Users expect instant gratification, and search engines reward fast websites. Let's dive into practical strategies to supercharge your web performance.

Why Performance Matters

  • User Experience: 53% of mobile users abandon sites that take over 3 seconds to load
  • SEO: Google uses Core Web Vitals as ranking factors
  • Conversions: Amazon found that every 100ms delay costs 1% in sales
  • Engagement: Faster sites have lower bounce rates and higher engagement

Core Web Vitals Explained

1. Largest Contentful Paint (LCP)

Target: < 2.5 seconds

LCP measures loading performance. To improve it:

typescript
// Use Next.js Image component
import Image from 'next/image';

<Image
  src="/hero.jpg"
  alt="Hero"
  width={1200}
  height={600}
  priority // Load above-the-fold images immediately
/>

2. First Input Delay (FID)

Target: < 100ms

FID measures interactivity. Improve it by:

javascript
// Split large tasks
async function processData(data) {
  const chunks = chunkArray(data, 100);
  
  for (const chunk of chunks) {
    await processChunk(chunk);
    // Yield to browser
    await new Promise(resolve => setTimeout(resolve, 0));
  }
}

3. Cumulative Layout Shift (CLS)

Target: < 0.1

Prevent layout shifts by reserving space:

css
/* Reserve space for images */
.image-container {
  aspect-ratio: 16 / 9;
  width: 100%;
}

/* Use transform for animations */
.animated-element {
  transform: translateY(10px); /* Good */
  /* top: 10px; Bad - causes reflow */
}

Optimization Strategies

1. Code Splitting

Load only what you need:

typescript
// Dynamic imports
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
  loading: () => <Skeleton />,
  ssr: false // Skip SSR if not needed
});

// Route-based splitting (automatic in Next.js)
export default function Page() {
  return <HeavyComponent />;
}

2. Image Optimization

typescript
// Use next/image for automatic optimization
import Image from 'next/image';

<Image
  src="/product.jpg"
  alt="Product"
  width={800}
  height={600}
  loading="lazy"
  placeholder="blur"
  blurDataURL="data:image/jpeg;base64,..."
/>

3. Font Optimization

typescript
// Use next/font for optimal loading
import { Inter } from 'next/font/google';

const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
  variable: '--font-inter'
});

export default function RootLayout({ children }) {
  return (
    <html className={inter.variable}>
      <body>{children}</body>
    </html>
  );
}

4. Lazy Loading

typescript
// Intersection Observer for lazy loading
function useLazyLoad() {
  const [isVisible, setIsVisible] = useState(false);
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsVisible(true);
          observer.disconnect();
        }
      },
      { rootMargin: '100px' }
    );

    if (ref.current) {
      observer.observe(ref.current);
    }

    return () => observer.disconnect();
  }, []);

  return { ref, isVisible };
}

5. Caching Strategies

typescript
// Implement SWR pattern
import useSWR from 'swr';

function Profile() {
  const { data, error, isLoading } = useSWR(
    '/api/user',
    fetcher,
    {
      revalidateOnFocus: false,
      dedupingInterval: 60000 // 1 minute
    }
  );

  if (isLoading) return <Skeleton />;
  if (error) return <Error />;
  return <div>{data.name}</div>;
}

6. Debouncing & Throttling

typescript
// Debounce search input
function useDebounce<T>(value: T, delay: number): T {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => clearTimeout(handler);
  }, [value, delay]);

  return debouncedValue;
}

// Usage
function SearchBar() {
  const [query, setQuery] = useState('');
  const debouncedQuery = useDebounce(query, 300);

  useEffect(() => {
    if (debouncedQuery) {
      search(debouncedQuery);
    }
  }, [debouncedQuery]);
}

Advanced Techniques

1. Prefetching

typescript
// Prefetch on hover
import { useRouter } from 'next/navigation';

function Link({ href, children }) {
  const router = useRouter();

  return (
    <a
      href={href}
      onMouseEnter={() => router.prefetch(href)}
    >
      {children}
    </a>
  );
}

2. Virtual Scrolling

For long lists, render only visible items:

typescript
import { useVirtualizer } from '@tanstack/react-virtual';

function VirtualList({ items }) {
  const parentRef = useRef<HTMLDivElement>(null);

  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 50,
  });

  return (
    <div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
      <div style={{ height: virtualizer.getTotalSize() }}>
        {virtualizer.getVirtualItems().map((virtualRow) => (
          <div key={virtualRow.index}>
            {items[virtualRow.index]}
          </div>
        ))}
      </div>
    </div>
  );
}

3. Web Workers

Offload heavy computations:

typescript
// worker.ts
self.onmessage = (e) => {
  const result = heavyComputation(e.data);
  self.postMessage(result);
};

// component.tsx
function HeavyTask() {
  useEffect(() => {
    const worker = new Worker('/worker.js');

    worker.postMessage(largeDataset);

    worker.onmessage = (e) => {
      console.log('Result:', e.data);
    };

    return () => worker.terminate();
  }, []);
}

Monitoring & Measurement

Tools to Use

1. Lighthouse - Comprehensive audits 2. WebPageTest - Detailed performance analysis 3. Chrome DevTools - Real-time profiling 4. Vercel Analytics - Production monitoring 5. Core Web Vitals - Google Search Console

Setting Up RUM

typescript
// Real User Monitoring
export function reportWebVitals(metric) {
  const body = JSON.stringify(metric);
  
  if (navigator.sendBeacon) {
    navigator.sendBeacon('/api/analytics', body);
  } else {
    fetch('/api/analytics', { body, method: 'POST', keepalive: true });
  }
}

Performance Checklist

  • [ ] Optimize images (WebP, AVIF)
  • [ ] Minimize JavaScript bundle size
  • [ ] Implement lazy loading
  • [ ] Use CDN for static assets
  • [ ] Enable compression (Brotli/Gzip)
  • [ ] Set up proper caching headers
  • [ ] Minimize render-blocking resources
  • [ ] Optimize CSS delivery
  • [ ] Use resource hints (preload, prefetch)
  • [ ] Monitor Core Web Vitals

Common Mistakes to Avoid

1. Loading everything upfront - Use code splitting 2. Not setting image dimensions - Causes CLS 3. Blocking the main thread - Use Web Workers 4. Ignoring mobile performance - Test on real devices 5. Not measuring in production - Set up RUM

Conclusion

Web performance is not a one-time task; it's an ongoing commitment. Start with the low-hanging fruit, measure the impact, and iterate. Your users (and your business metrics) will thank you.

Remember: Fast websites win.

---

What performance optimization techniques have worked for you? Let me know in the comments!
Get More Like This

Want articles like this in your inbox?

Join developers and founders who get practical insights on frontend, SaaS, and building better products.

S

Written by Salman Izhar

Frontend Developer specializing in React, Next.js, and building high-converting web applications.

Learn More