Rendimiento en Next.js
Reglas
- Imágenes: siempre usar
next/image con sizes explícitos, priority solo para la imagen LCP hero. Fuentes: next/font (sin bloqueo de renderizado por enlaces <link> de Google).
- Medir primero:
@next/bundle-analyzer, salida de next build, Lighthouse. Corregir el fragmento más grande primero.
dynamic(() => import(...), { ssr: false }) para widgets pesados solo del lado del cliente (gráficos, editores). Mantener el JS inicial por debajo de 200 kB gz cuando sea posible.
- Almacenar en caché de forma agresiva: estático +
revalidate (ISR) para páginas semidinámicas; usar loading.tsx/Suspense para transmitir partes lentas en lugar de bloquear TTFB.
- Evitar cascadas: paralelizar en RSC, memorizar
fetch repetidos (desduplicación automática por solicitud), usar unstable_cache para cómputos costosos.
Patrones
import Image from 'next/image';
<Image src="/hero.jpg" alt="Dashboard" width={1200} height={630} priority sizes="100vw" />
<Image src="/avatar.png" alt="Usuario" width={64} height={64} sizes="64px" />
import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'], display: 'swap' });
export default function RootLayout({ children }: { children: React.ReactNode }) {
return <html lang="es" className={inter.className}><body>{children}</body></html>;
}
import dynamic from 'next/dynamic';
const Chart = dynamic(() => import('@/components/chart'), { ssr: false, loading: () => <p>Cargando gráfico…</p> });
export const revalidate = 60;
const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true' });
module.exports = withBundleAnalyzer({});
Lista de verificación