How I think about performance in Next.js
Practical strategies to optimize Next.js applications and achieve perfect Core Web Vitals.
Web performance is not just about "making the site load fast." It is about user experience, conversion, and ultimately, revenue. When we work with Next.js, we have a powerful framework in hand, but it is easy to fall into traps that degrade performance if we don't pay attention to the right details.
The Performance Mindset
Many developers leave performance until the end. "First make it work, then optimize." While premature optimization is a problem, neglecting architectural performance is worse. Performance must be a feature, not a fix.
1. Images are the Number 1 Villain
The next/image component is fantastic, but it doesn't perform miracles if you don't use it correctly.
- Always use
sizes: The most common mistake is failing to define thesizesattribute. Without it, Next.js might serve a huge image to a mobile device. - Modern formats: Next.js automatically serves WebP/AVIF, but ensure your original source image has sufficient quality.
- LCP (Largest Contentful Paint): The image that constitutes the LCP (usually the hero or banner) must have the
priorityprop. This tells the browser to load it immediately, bypassing the default lazy loading.
// Bad for LCP
<Image src="/hero.jpg" width={800} height={600} alt="Hero" />
// Good for LCP
<Image src="/hero.jpg" width={800} height={600} alt="Hero" priority />2. Fonts and Layout Shift (CLS)
Cumulative Layout Shift (CLS) is annoying and penalizes your SEO. Late-loading fonts are frequent culprits.
- Use
next/font: This downloads the font at build time and self-hosts it locally, removing the round-trip to Google Fonts. - Use
display: swaporoptionalwith well-adjusted fallback fonts.
3. Bundle Size and Dynamic Imports
Next.js automatically performs code splitting per route. But heavy components within a page (like a rich text editor, complex charts, or maps) should be loaded on demand.
import dynamic from 'next/dynamic'
const HeavyChart = dynamic(() => import('@/components/HeavyChart'), {
loading: () => <p>Loading chart...</p>,
ssr: false // If you don't need SEO for this component
})4. Rendering: Server vs Client
The modern golden rule (React Server Components): Move as much as possible to the server.
- Server Components: Zero JavaScript sent to the client. Great for rendering markdown, fetching data, etc.
- Client Components: Use only where interactivity is needed (
useState,useEffect, event listeners).
The less JS the browser has to download, parse, and execute, the better the TBT (Total Blocking Time) and INP (Interaction to Next Paint).
Conclusion
Performance in Next.js is about leveraging the tools the framework provides (Image, Font, Script) and making conscious decisions about what is rendered where. Monitor your Core Web Vitals and treat performance regressions as critical bugs.