Next.js Performance Analytics — Client & Server Usage Guide
Overview
Performance in a Next.js application depends on multiple factors such as JavaScript bundle size, server component efficiency, navigation speed, image optimization, script loading, and key Web Vitals metrics. Proper monitoring and optimization in these areas ensures your app is fast, responsive, and SEO-friendly.
Client-Side Performance Checks
On the client side, performance issues often arise from heavy JavaScript bundles, inefficient client components, and unoptimized resource loading.
1. JavaScript Bundle Size
Large bundles slow page load and interactivity.
npm install @next/bundle-analyzernext.config.js:
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true',
});
module.exports = withBundleAnalyzer({});ANALYZE=true npm run buildChecks:
- Look for large libraries (MUI, Lodash, Chart.js)
- Repeated code across chunks
- Unoptimized imports
Fixes:
- Convert components to Server Components where possible
- Lazy-load heavy components with
dynamic() - Tree-shake libraries
- Remove unused dependencies
2. Navigation Lag
Lag happens when route changes trigger heavy re-renders or unnecessary fetches.
Checks:
- Are data fetching calls repeated on every route change?
- Are layouts heavy or contain many client components?
Fixes:
- Use React Suspense with
loading.js - Cache requests (
fetch(..., { cache: "force-cache" })) - Keep layouts lightweight
- Move heavy logic to server components
3. Images & Scripts
Images (next/image)
Optimized images improve LCP and reduce CLS.
Best Practices:
- Set
widthandheightto prevent layout shifts - Use
priorityfor hero images - Use
placeholder="blur"for smoother loading - Include
altfor SEO
Scripts (next/script)
Strategy Options:
| Strategy | Loads When | Use Case |
|---|---|---|
beforeInteractive | Immediately | Critical scripts |
afterInteractive | After hydration | Analytics |
lazyOnload | Idle time | Ads, trackers |
Fixes:
- Avoid blocking hydration
- Load non-critical scripts lazily
- Minimize main-thread work
Server-Side Performance (SSR/SSG)
Server-side performance affects TTFB, LCP, and overall page load.
1. Server Components
Checks:
- Are heavy computations inside server components?
- Are large datasets fetched without caching?
Fixes:
- Cache queries (
revalidate, Redis, or CDN) - Split heavy logic into smaller components
- Paginate large datasets
- Use server actions for mutations
2. Prefetching Data
Prefetch data during SSR to reduce client-side fetching and improve UX.
import { QueryClient, dehydrate } from '@tanstack/react-query'
const queryClient = new QueryClient()
await queryClient.prefetchQuery({
queryKey: ['users'],
queryFn: () => fetch('/api/users').then(res => res.json())
})
const dehydratedState = dehydrate(queryClient)- Use
HydrationBoundaryto pass the prefetched data to the client - Prevents unnecessary network requests after initial render
Core Performance Metrics (Web Vitals)
Next.js provides the useReportWebVitals hook to monitor performance:
import { useReportWebVitals } from 'next/web-vitals'
useReportWebVitals(metric => console.log(metric))Or you can use Lighthouse extension to look further into this performance metrics.
Metrics Overview
| Metric | Description | Target | Why It Matters | Common Causes | Fixes |
|---|---|---|---|---|---|
| LCP | Largest Content full Paint: time until the largest visible element renders | < 2.5s | Measures perceived load speed | Large images, slow server response, render-blocking JS/CSS | Optimize images, cache server responses, reduce JS bundle, use Server Components |
| INP | Interaction to Next Paint: responsiveness to user input | < 200ms | Measures interactivity | Heavy JS, hydration delay, client-side re-renders | Move logic to server, memoize components, virtualize lists |
| CLS | Cumulative Layout Shift: layout stability | < 0.1 | Prevents visual jank | Images without dimensions, font swap, dynamic content | Set width/height, use font-display swap, reserve layout space |
| TBT | Total Blocking Time: main-thread blocking by JS | < 300ms | Affects interactivity and INP | Large JS bundles, heavy hydration | Split JS, lazy-load components, convert to Server Components |
| Hydration Time | Time for React to attach to SSR HTML | Fast as possible | Affects time to interactive | Many client components, heavy JS | Reduce client components, dynamic imports, partial hydration |
Recommended Docs
For the most up-to-date and detailed information, please refer to the official documentation:
Summary Checklist
| Area | What to Monitor | Common Fixes |
|---|---|---|
| Bundle size | JS size, libraries | Lazy load, tree-shake, remove unused deps |
| Server components | Heavy logic, data fetching | Cache, split components, server actions |
| Navigation | Lag during route changes | Suspense, cache, lightweight layouts |
| Images | LCP & CLS | next/image, width/height, alt |
| Scripts | Hydration blocking | Script strategies (lazyOnload/afterInteractive) |
| Metrics | LCP, INP, CLS, TBT, Hydration | Monitor via useReportWebVitals and Lighthouse |