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 la mejor experiencia de usuario, rendimiento y seguridad.
Esta página proporciona buenas prácticas que puedes usar como referencia cuando construyas tu aplicación y antes de ir a producción, así como las optimizaciones automáticas de Next.js que deberías 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. Los Componentes del Servidor 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 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 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 tiempo de construcción y almacena en caché el resultado para mejorar el rendimiento de tu aplicación. Puedes optar por Renderizado dinámico (Dynamic Rendering) para rutas específicas cuando sea apropiado.
- Almacenamiento en caché (Caching): Next.js almacena en caché solicitudes de datos, el resultado renderizado de Componentes del Servidor y del Cliente, activos estáticos y más, para reducir el número de solicitudes de red a tu servidor, base de datos y servicios backend. Puedes optar por no usar caché cuando sea apropiado.
Estos valores predeterminados buscan mejorar el rendimiento de tu aplicación y reducir el costo y la 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
- Diseños (Layouts): Usa diseños 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 opte por Renderizado dinámico (Dynamic Rendering) (o toda tu aplicación si se usan en el Diseño raíz (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: Prerrenderizado parcial (experimental) permitirá que partes de una ruta sean dinámicas sin optar por renderizado dinámico en toda la ruta.
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 Manejadores de Ruta para acceder a tus recursos backend desde Componentes del Cliente. Pero no llames a Manejadores de Ruta desde Componentes del Servidor para evitar una solicitud adicional al servidor.
- Streaming: Usa UI de carga y React Suspense para enviar progresivamente UI desde el servidor al cliente, y evitar que toda la ruta se bloquee mientras se obtienen los datos.
- Obtención paralela de datos (Parallel Data Fetching): Reduce las cascadas de red obteniendo datos en paralelo cuando sea apropiado. También considera precargar datos (preloading data) cuando sea apropiado.
- 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 activos 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 Módulo de Fuentes, que aloja automáticamente tus archivos de fuente con otros activos estáticos, elimina solicitudes de red externas y reduce cambio de diseño (layout shift).
- Componente
<Image>
: Optimiza imágenes usando el Componente Image, que optimiza automáticamente las imágenes, evita cambios de diseño y las sirve en formatos modernos como WebP. - Componente
<Script>
: Optimiza scripts de terceros usando el Componente Script, que difiere automáticamente los scripts y evita que bloqueen el hilo principal. - ESLint: Usa el plugin incorporado
eslint-plugin-jsx-a11y
para detectar problemas de accesibilidad temprano.
Seguridad
- Tainting: Evita 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 a Acciones del Servidor. Revisa las prácticas de seguridad recomendadas.
- Variables de entorno (Environment Variables): Asegúrate de que tus archivos
.env.*
estén agregados a.gitignore
y solo las variables públicas tengan el prefijoNEXT_PUBLIC_
. - Política de seguridad de contenido (Content Security Policy): Considera agregar una Política de Seguridad de Contenido para proteger tu aplicación contra varias 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 API de Metadatos 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.
- Mapas del sitio (Sitemaps) y Robots: Ayuda a los motores de búsqueda a rastrear e indexar tus páginas generando mapas del sitio y archivos robots.
Seguridad de tipos
- TypeScript y Plugin TS: Usa TypeScript y el plugin de TypeScript para mayor seguridad de tipos y para ayudarte a 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 cualquier error de construcción, 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 experimentarán los usuarios tu sitio e identificar áreas de mejora. Esta es una prueba simulada y debe combinarse con datos de campo (como Core Web Vitals).
- Hook
useReportWebVitals
: Usa este hook para enviar datos de Core Web Vitals a herramientas de análisis.
Analizando 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: