Next.js Performance en 2025: Guía Definitiva (con checklist)
Mejora Core Web Vitals en Next.js: imágenes, RSC, caché, code splitting y auditorías. Guía práctica con checklist.
Next.js Performance en 2025: Guía Definitiva
TL;DR
Optimiza imágenes con next/image
(+sizes
), usa Server Components por defecto, separa cliente con 'use client'
, activa caché/ISR, divide código crítico y mide con Lighthouse/Core Web Vitals.
🚀 Importante: Performance es un proceso iterativo. No trates de implementar todo de una vez.
0. Checklist rápido
-
next/image
consizes
ypriority
solo above-the-fold - Fuentes con
next/font
(display: 'swap'
,preload: true
en críticas) - Server Components by default; Client solo si hay estado/efectos
-
dynamic()
+suspense
para UX fluida - Caching: ISR/Route Segment Config (
revalidate
), Edge donde haga sentido -
@next/bundle-analyzer
para podar dependencias - Prefetch selectivo de enlaces críticos (
<Link prefetch>
)
1. Comparación de Métricas Core Web Vitals
Métrica | Sin Optimizar | Con Optimización | Mejora |
---|---|---|---|
LCP | 4.2s | 1.8s | 57% |
FID | 280ms | 45ms | 84% |
CLS | 0.25 | 0.05 | 80% |
Datos basados en testing real con Lighthouse
2. Image Optimization con next/image
Configuración básica
Incluye sizes
para evitar servir imágenes sobredimensionadas y reducir CLS:
import Image from 'next/image'
export function OptimizedImage() {
return (
<Image
src="/hero-image.jpg"
alt="Hero con producto"
width={1200}
height={800}
priority
placeholder="blur"
blurDataURL="data:image/jpeg;base64,..."
sizes="(max-width: 768px) 100vw, 1200px"
/>
)
}
Técnicas avanzadas
- Responsive images: Usa
sizes
correctamente - Lazy loading: Por defecto en
next/image
- WebP/AVIF: Automático con Next.js
- Placeholder blur: Mejora UX durante carga
3. Code Splitting y Lazy Loading
En app router, dynamic
+ suspense
para componentes pesados:
import dynamic from 'next/dynamic'
import { Suspense } from 'react'
const Heavy = dynamic(() => import('../components/Heavy'), {
ssr: false,
loading: () => <p>Cargando componente...</p>
})
export default function Page() {
return (
<Suspense fallback={<div className="animate-pulse">Cargando...</div>}>
<Heavy />
</Suspense>
)
}
4. Optimización de Fuentes
Evita FOIT/FOUT con next/font
y variable CSS:
import { Inter, Sora } from 'next/font/google'
export const inter = Inter({
subsets: ['latin'],
display: 'swap',
variable: '--font-inter',
preload: true
})
export const sora = Sora({
subsets: ['latin'],
display: 'swap',
variable: '--font-sora'
})
Buenas prácticas para fuentes:
- ✅ Usa
font-display: swap
- ✅ Preload solo fuentes críticas
- ✅ Limita el número de font weights
- ❌ No uses Google Fonts directo en CSS
5. Server vs Client Components
Regla de oro: Server por defecto, Client solo cuando necesites
// ✅ Server Component (por defecto)
export default async function Products() {
const data = await getData() // se ejecuta en el server
return <ProductList data={data} />
}
// ✅ Client Component (solo cuando necesites estado/eventos)
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return (
<button
onClick={() => setCount(count + 1)}
className="px-4 py-2 bg-blue-500 text-white rounded"
>
Clicks: {count}
</button>
)
}
6. Bundle Analysis y Tree Shaking
Instalar analyzer
npm install --save-dev @next/bundle-analyzer
Configurar next.config.js
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: process.env.ANALYZE === 'true'
})
module.exports = withBundleAnalyzer({
experimental: {
optimizePackageImports: ['lodash', 'date-fns', 'lucide-react']
},
images: {
formats: ['image/avif', 'image/webp'],
remotePatterns: [
{
protocol: 'https',
hostname: '**',
},
],
}
})
7. Estrategias de Caché y ISR
Por página
// app/products/page.tsx
export const revalidate = 3600 // ISR cada hora
export default async function ProductsPage() {
const products = await getProducts()
return <ProductGrid products={products} />
}
Por request
// Para datos que cambian frecuentemente
export const dynamic = 'force-dynamic'
export const runtime = 'edge' // opcional: usar Edge Runtime
8. Monitoreo y Métricas
Web Vitals tracking
// app/layout.tsx
import { Analytics } from '@vercel/analytics/react'
import { SpeedInsights } from '@vercel/speed-insights/next'
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Analytics />
<SpeedInsights />
</body>
</html>
)
}
Custom performance tracking
'use client'
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals'
export function WebVitals() {
useEffect(() => {
getCLS(console.log)
getFID(console.log)
getFCP(console.log)
getLCP(console.log)
getTTFB(console.log)
}, [])
return null
}
Roadmap de Optimización
Fase 1: Fundamentos (Semana 1)
- Configurar
next/image
en todas las imágenes - Implementar
next/font
para todas las fuentes - Auditoría inicial con Lighthouse
Fase 2: Arquitectura (Semana 2)
- Separar Server/Client components
- Implementar lazy loading en componentes pesados
- Configurar ISR en páginas estáticas
Fase 3: Optimización avanzada (Semana 3)
- Bundle analysis y tree shaking
- Edge Runtime donde sea apropiado
- Implementar monitoreo continuo
Conclusión
La performance no es un destino, es un viaje
Performance en Next.js es un proceso iterativo: mide → prioriza → implementa → valida.
Empieza siempre por:
- 🖼️ Imágenes (mayor impacto, menor esfuerzo)
- 🔤 Fuentes (mejora percepción de velocidad)
- 🗄️ Caché (reduce carga del servidor)
- 📦 Bundle (menos JavaScript = más velocidad)
¿Qué técnica vas a implementar primero? ¡Compárteme tus resultados!
Links útiles: