Web Performance Optimization: A Practical Guide
Web performance directly impacts user experience, conversion rates, and SEO rankings. This guide covers practical optimization techniques you can apply today.
Why Performance Matters
Studies show that:
- 53% of mobile users abandon sites that take >3 seconds to load
- A 100ms delay can decrease conversion rates by 7%
- Google uses page speed as a ranking factor
Let's optimize!
Core Web Vitals
Google's Core Web Vitals measure user experience:
1. Largest Contentful Paint (LCP)
Target: < 2.5 seconds
Optimize:
- Use
next/imagefor automatic optimization - Implement proper caching headers
- Preload critical resources
<link rel="preload" href="/hero-image.jpg" as="image" />
2. First Input Delay (FID)
Target: < 100ms
Optimize:
- Minimize JavaScript execution time
- Split long tasks
- Use web workers for heavy computations
3. Cumulative Layout Shift (CLS)
Target: < 0.1
Optimize:
- Always specify image dimensions
- Reserve space for dynamic content
- Avoid inserting content above existing content
<Image
src="/image.jpg"
width={800}
height={600}
alt="Descriptive text"
/>
Bundle Size Optimization
Code Splitting
Split code by route automatically with Next.js:
// Automatic code splitting
import dynamic from 'next/dynamic'
const HeavyComponent = dynamic(() => import('./HeavyComponent'), {
loading: () => <Spinner />,
ssr: false
})
Tree Shaking
Remove unused code:
// Bad: Imports entire library
import _ from 'lodash'
// Good: Imports only what's needed
import debounce from 'lodash/debounce'
// Better: Use native methods
const debounce = (fn, delay) => {
let timeoutId
return (...args) => {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => fn(...args), delay)
}
}
Bundle Analysis
Analyze your bundle:
npm install @next/bundle-analyzer
# next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true'
})
module.exports = withBundleAnalyzer({
// config
})
Image Optimization
Next.js Image Component
Always use next/image:
import Image from 'next/image'
<Image
src="/photo.jpg"
alt="Description"
width={800}
height={600}
sizes="(max-width: 768px) 100vw, 50vw"
priority={false} // Set true for above-fold images
/>
Modern Formats
Configure modern image formats:
// next.config.js
module.exports = {
images: {
formats: ['image/avif', 'image/webp'],
deviceSizes: [640, 768, 1024, 1280, 1536],
},
}
Lazy Loading Strategies
Component Lazy Loading
Load components on demand:
'use client'
import { lazy, Suspense } from 'react'
const Chart = lazy(() => import('./Chart'))
export function Dashboard() {
return (
<Suspense fallback={<ChartSkeleton />}>
<Chart data={data} />
</Suspense>
)
}
Intersection Observer
Load content when visible:
'use client'
import { useEffect, useRef, useState } from 'react'
export function LazyComponent() {
const ref = useRef(null)
const [isVisible, setIsVisible] = useState(false)
useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
setIsVisible(true)
observer.disconnect()
}
},
{ threshold: 0.1 }
)
if (ref.current) {
observer.observe(ref.current)
}
return () => observer.disconnect()
}, [])
return (
<div ref={ref}>
{isVisible ? <HeavyComponent /> : <Placeholder />}
</div>
)
}
Caching Strategies
Static Generation
Pre-render pages at build time:
// Fully static
export default async function Page() {
const data = await fetchData()
return <Component data={data} />
}
// With revalidation (ISR)
export const revalidate = 3600 // Revalidate every hour
HTTP Caching
Configure caching headers:
export async function GET() {
return new Response(JSON.stringify({ data }), {
headers: {
'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400',
},
})
}
Performance Monitoring
Web Vitals Tracking
Track Core Web Vitals:
// app/layout.tsx
import { Analytics } from '@vercel/analytics/react'
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
</body>
</html>
)
}
Lighthouse CI
Automate performance testing:
# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [push]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm install && npm run build
- uses: treosh/lighthouse-ci-action@v10
Quick Wins Checklist
- [ ] Use
next/imagefor all images - [ ] Enable static generation where possible
- [ ] Implement code splitting for heavy components
- [ ] Optimize fonts with
next/font - [ ] Minimize client-side JavaScript
- [ ] Configure proper caching headers
- [ ] Compress assets (automatic with Vercel)
- [ ] Use CDN for static assets
- [ ] Implement lazy loading for below-fold content
- [ ] Monitor with Lighthouse and Web Vitals
Conclusion
Performance optimization is an ongoing process. Key principles:
- Measure first - Use Lighthouse and Real User Monitoring
- Optimize critical path - Prioritize above-fold content
- Reduce JavaScript - Use Server Components when possible
- Lazy load - Load what users need, when they need it
- Monitor continuously - Set up automated performance testing
Remember: Fast sites make happy users!
What's your favorite performance optimization technique? Share below!