Cómo optimizar tu aplicación Next.js para producción
Antes de llevar tu aplicación Next.js a producción, hay algunas optimizaciones y patrones que deberías considerar implementar para obtener la mejor experiencia de usuario, rendimiento y seguridad.
Esta página proporciona mejores prácticas que puedes usar como referencia durante el desarrollo de tu aplicación y antes de ir a producción, así como las optimizaciones automáticas de Next.js que debes conocer.
Optimizaciones automáticas
Estas optimizaciones de Next.js están habilitadas por defecto y no requieren configuración:
- Componentes del servidor (Server Components): Next.js usa Componentes del Servidor por defecto. Estos componentes se ejecutan en el servidor y no requieren JavaScript para renderizarse en el cliente. Por lo tanto, no impactan el tamaño de tus paquetes JavaScript del lado del cliente. Puedes usar Componentes del Cliente (Client Components) según sea necesario para interactividad.
- División de código (Code-splitting): Los Componentes del Servidor permiten la división automática de código por segmentos de ruta. También puedes considerar la carga diferida (lazy loading) de Componentes del Cliente y bibliotecas de terceros cuando sea apropiado.
- Precarga (Prefetching): Cuando un enlace a una nueva ruta entra en el viewport del usuario, Next.js precarga la ruta en segundo plano. Esto hace que la navegación a nuevas rutas sea casi instantánea. Puedes optar por no usar la precarga cuando sea apropiado.
- Renderizado estático (Static Rendering): Next.js renderiza estáticamente Componentes del Servidor y del Cliente en el servidor durante el build y almacena en caché el resultado para mejorar el rendimiento. Puedes optar por el Renderizado dinámico (Dynamic Rendering) para rutas específicas cuando sea necesario.
- Almacenamiento en caché (Caching): Next.js almacena en caché solicitudes de datos, resultados renderizados de Componentes del Servidor y del Cliente, recursos estáticos y más, para reducir solicitudes de red a tu servidor, base de datos y servicios backend. Puedes optar por no usar caché cuando sea apropiado.
Estos valores por defecto buscan mejorar el rendimiento de tu aplicación y reducir el costo y cantidad de datos transferidos en cada solicitud de red.
Durante el desarrollo
Mientras construyes tu aplicación, recomendamos usar las siguientes características para garantizar el mejor rendimiento y experiencia de usuario:
Enrutamiento y renderizado
- Layouts: Usa layouts para compartir UI entre páginas y habilitar renderizado parcial (partial rendering) en la navegación.
- Componente
<Link>
: Usa el componente<Link>
para navegación del lado del cliente y precarga (client-side navigation and prefetching). - Manejo de errores (Error Handling): Maneja adecuadamente errores catch-all y errores 404 en producción creando páginas de error personalizadas.
- Componentes del Cliente y del Servidor (Client and Server Components): Sigue los patrones de composición recomendados para Componentes del Servidor y del Cliente, y verifica la ubicación de tus límites
"use client"
para evitar aumentar innecesariamente tu paquete JavaScript del lado del cliente. - APIs dinámicas (Dynamic APIs): Ten en cuenta que APIs dinámicas como
cookies
y la propsearchParams
harán que toda la ruta use Renderizado dinámico (Dynamic Rendering) (o toda tu aplicación si se usan en el Root Layout). Asegúrate de que el uso de APIs dinámicas sea intencional y envuélvelas en límites<Suspense>
cuando sea apropiado.
Nota importante: El renderizado parcial pre-generado (Partial Prerendering, experimental) permitirá que partes de una ruta sean dinámicas sin hacer que toda la ruta use renderizado dinámico.
Obtención de datos y almacenamiento en caché
- Componentes del Servidor (Server Components): Aprovecha los beneficios de obtener datos en el servidor usando Componentes del Servidor.
- Manejadores de ruta (Route Handlers): Usa Route Handlers para acceder a recursos backend desde Componentes del Cliente. Pero no llames Route Handlers desde Componentes del Servidor para evitar solicitudes adicionales al servidor.
- Streaming: Usa UI de carga y React Suspense para enviar progresivamente UI del servidor al cliente, evitando que toda la ruta se bloquee mientras se obtienen datos.
- Obtención paralela de datos (Parallel Data Fetching): Reduce cascadas de red obteniendo datos en paralelo cuando sea apropiado. También considera precargar datos (preloading data) cuando corresponda.
- Almacenamiento en caché de datos (Data Caching): Verifica si tus solicitudes de datos están siendo almacenadas en caché o no, y opta por el almacenamiento en caché cuando sea apropiado. Asegúrate de que las solicitudes que no usan
fetch
estén almacenadas en caché. - Imágenes estáticas (Static Images): Usa el directorio
public
para almacenar automáticamente en caché los recursos estáticos de tu aplicación, como imágenes.
UI y accesibilidad
- Formularios y validación (Forms and Validation): Usa Acciones del Servidor (Server Actions) para manejar envíos de formularios, validación del lado del servidor y manejo de errores.
- Módulo de fuentes (Font Module): Optimiza fuentes usando el Font Module, que aloja automáticamente tus archivos de fuentes con otros recursos estáticos, elimina solicitudes de red externas y reduce layout shift.
- Componente
<Image>
: Optimiza imágenes usando el componente Image, que optimiza automáticamente imágenes, previene layout shift y las sirve en formatos modernos como WebP. - Componente
<Script>
: Optimiza scripts de terceros usando el componente Script, que difiere automáticamente scripts y evita que bloqueen el hilo principal. - ESLint: Usa el plugin integrado
eslint-plugin-jsx-a11y
para detectar problemas de accesibilidad temprano.
Seguridad
- Tainting: Previene que datos sensibles se expongan al cliente marcando objetos de datos y/o valores específicos.
- Acciones del Servidor (Server Actions): Asegúrate de que los usuarios estén autorizados para llamar Acciones del Servidor. Revisa las prácticas de seguridad recomendadas.
- Variables de entorno (Environment Variables): Asegúrate de que tus archivos
.env.*
estén en.gitignore
y solo las variables públicas tengan prefijoNEXT_PUBLIC_
. - Política de seguridad de contenido (Content Security Policy): Considera agregar una CSP para proteger tu aplicación contra amenazas de seguridad como cross-site scripting, clickjacking y otros ataques de inyección de código.
Metadatos y SEO
- API de Metadatos (Metadata API): Usa la Metadata API para mejorar el SEO de tu aplicación agregando títulos de página, descripciones y más.
- Imágenes Open Graph (OG): Crea imágenes OG para preparar tu aplicación para compartir en redes sociales.
- Sitemaps y Robots: Ayuda a los motores de búsqueda a rastrear e indexar tus páginas generando sitemaps y archivos robots.
Seguridad de tipos
- TypeScript y TS Plugin: Usa TypeScript y el plugin de TypeScript para mayor seguridad de tipos y detectar errores temprano.
Antes de ir a producción
Antes de ir a producción, puedes ejecutar next build
para construir tu aplicación localmente y detectar errores de build, luego ejecuta next start
para medir el rendimiento de tu aplicación en un entorno similar a producción.
Core Web Vitals
- Lighthouse: Ejecuta Lighthouse en modo incógnito para entender mejor cómo los usuarios experimentarán tu sitio e identificar áreas de mejora. Esta es una prueba simulada y debe complementarse con datos de campo (como Core Web Vitals).
- Hook
useReportWebVitals
: Usa este hook para enviar datos de Core Web Vitals a herramientas de analítica.
Análisis de paquetes
Usa el plugin @next/bundle-analyzer
para analizar el tamaño de tus paquetes JavaScript e identificar módulos y dependencias grandes que puedan estar afectando el rendimiento de tu aplicación.
Adicionalmente, las siguientes herramientas pueden ayudarte a entender el impacto de agregar nuevas dependencias a tu aplicación: