Performance in Next.js is not an accident; it is the result of intentional engineering. However, without a Performance Budget, speed often degrades over time as new features, heavy libraries, and unoptimized images are added to the codebase.
In this guide, we explore how to set a performance budget that keeps your Core Web Vitals in the "Green" forever.
What is a Performance Budget?
A performance budget is a set of limits you impose on your application to ensure it meets specific speed goals. These limits can be:
- Quantity-based: Max JS bundle size (e.g., < 200KB).
- Timing-based: Max Time to Interactive (e.g., < 2.5s).
- Score-based: Minimum Lighthouse Performance score (e.g., > 95).
1. Defining Your Metrics
For Next.js apps, focus on the "Big Three" Core Web Vitals:
- LCP (Largest Contentful Paint): Measure of loading performance. Aim for < 2.5s.
- INP (Interaction to Next Paint): Measure of responsiveness. Aim for < 200ms.
- CLS (Cumulative Layout Shift): Measure of visual stability. Aim for < 0.1.
2. Optimizing the Critical Path
Next.js provides built-in components to help you stay within budget.
Image Optimization (next/image)
Images are usually the largest part of a website. Always use the Next.js Image component to:
- Automatically serve WebP/AVIF.
- Implement lazy loading out of the box.
- Use the
priorityattribute for Above-the-Fold images to boost LCP.
Script Loading (next/script)
Third-party scripts (Analytics, Ads, Chatbots) are performance killers. Use the strategy prop:
afterInteractive: Load after the page is hydrated.lazyOnload: Load during idle time.worker: (Experimental) Load in a web worker.
3. Bundle Analysis
You cannot manage what you cannot measure. Use the @next/bundle-analyzer to visualize what is making your JS files heavy.
The Goal: Keep your first-load JS under 150KB (compressed). If a library like moment.js or lodash is bloating your bundle, look for lightweight alternatives like date-fns or native ES6 methods.
4. Code Splitting & Dynamic Imports
Use next/dynamic to lazy-load components that aren't immediately visible. For example, a heavy Modal or a complex Chart should only be loaded when the user interacts with the page.
import dynamic from 'next/dynamic'
const HeavyChart = dynamic(() => import('../components/HeavyChart'), {
loading: () => <p>Loading...</p>,
ssr: false,
})
5. Automated Enforcement
A budget is useless if it isn't enforced.
- Use Lighthouse CI in your GitHub Actions to block PRs that drop the performance score below your threshold.
- Set up BundleWatch to fail builds if a JS file exceeds its allocated size.
Conclusion
A performance budget is a contract with your users. It says: "We value your time more than we value adding a heavy, unnecessary feature." By setting strict limits and using the power of Next.js, you can build applications that stay fast, regardless of how much they grow.
Stop guessing, start measuring, and stick to your budget.
Don't forget the human element: A fast site must also be an inclusive one. Check out our Ultimate Web Accessibility Audit Checklist to learn more.