¿Qué versión elegir? Next.js 16 frente a 15
El curso usa Next.js 16.4, que es la versión Active LTS: la política de Next.js mantiene cada versión mayor en soporte activo hasta que sale la siguiente. La anterior, Next.js 15, está en Maintenance LTS y recibe parches de seguridad hasta el 31 de enero de 2027.
Muchos proyectos siguen en Next.js 15, y el paso a la 16 es grande: cambian la caché, las APIs de petición, middleware y next/image. Esta página reúne todos los apartados del curso que no funcionan igual en Next.js 15, con lo que tienes que hacer en su lugar.
| Marca | Significado |
|---|---|
| 🆕 | No existe en Next.js 15 |
| 🔄 | Existe en Next.js 15, pero cambia: API, valor por defecto o comportamiento |
⚠️ Importante: Next.js 15 deja de recibir parches de seguridad el 31 de enero de 2027. Para un proyecto nuevo, usa Next.js 16; el módulo 24 explica cómo actualizar.
Resumen
El curso tiene 134 apartados que dependen de la versión, en 26 módulos.
| Versión | 🆕 Nuevo | 🔄 Cambia |
|---|---|---|
| 16.0 | 25 | 75 |
| 16.1 | 3 | 0 |
| 16.2 | 5 | 1 |
| 16.3 | 15 | 3 |
| 16.4 | 5 | 2 |
Detalle por módulo
00 - Introducción a Next.js
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
| 10.2. Un mismo código, dos comportamientos | 🆕 16.0 | La opción cacheComponents no existe en Next.js 15. | Una página que lee datos sin marcar se prerenderiza en el build con los datos de ese momento; para que se genere en cada petición añade export const dynamic = 'force-dynamic' a la página (en Next.js 16 con cacheComponents esa línea rompe el build). |
| 11. Cómo leer las marcas de este curso | 🔄 16.0 | En Next.js 15 el fichero se llama middleware.ts y exporta una función middleware, que por defecto se ejecuta en el runtime Edge. | Usa middleware.ts con el mismo contenido y la función llamada middleware. |
| 11. Cómo leer las marcas de este curso | 🆕 16.4 | No existe en Next.js 15. | No hay equivalente directo; … |
01 - Entorno y create-next-app
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
| 1.1 Node.js | 🔄 16.0 | En Next.js 15 la versión mínima de Node.js es 18.18, y en Next.js 16 es la 20.9. | Puedes seguir con Node 18.18 o posterior, aunque Node 18 ya no recibe parches de seguridad; usa igualmente Node 20.9 o superior y no tendrás que cambiar nada al actualizar. |
| 1.4 Navegadores soportados | 🔄 16.0 | Next.js 16 sube el mínimo a Chrome 111, Edge 111, Firefox 111 y Safari 16.4, y genera código para esos navegadores. | El mínimo es más bajo (Next 15 todavía admitía navegadores algo más antiguos); si necesitas dar soporte a navegadores anteriores a los de la lista, es un motivo real para quedarte en 15 o para añadir polyfills. |
| 2.5 Qué genera Next.js 16 frente a Next.js 15 | 🔄 16.0 | La plantilla de create-next-app 15.5 con --yes no instala ESLint, usa Tailwind a través de PostCSS, deja next.config.ts vacío y añade --turbopack a los scripts. | Crea el proyecto con npx create-next-app@15 mi-app --yes --eslint si quieres ESLint, y ten en cuenta que no tendrás AGENTS.md, Cache Components ni los tipos de next typegen en tsconfig.json hasta que actualices. |
3.4 next.config.ts | 🆕 16.0 | cacheComponents es una opción estable de primer nivel desde Next.js 16.0, y partialPrefetching llega en la 16.3. En Next.js 15 no existen como opciones estables (solo experimental.cacheComponents, experimental.ppr y experimental.dynamicIO, pensadas para las versiones canary). | Deja next.config.ts sin ellas y usa el modelo de caché de Next 15 (fetch con cache y next.revalidate, export const revalidate, unstable_cache), que se explica en el módulo 09. |
| 5.2 Turbopack por defecto | 🔄 16.0 | En Next.js 15 el bundler por defecto es webpack, y Turbopack se activa con --turbopack en los scripts (la plantilla 15.5 ya lo hace en dev y build). En Next.js 16 Turbopack es el predeterminado en next dev y next build, y webpack se pide con --webpack. | Mantén next dev --turbopack y next build --turbopack en los scripts, como hace la plantilla; sin la opción usarás webpack. |
| 5.3 La caché en disco | 🆕 16.1 | Turbopack guarda su trabajo en disco entre ejecuciones de next dev (en .next/dev/cache/turbopack) desde la 16.1, y también entre ejecuciones de next build (en .next/cache/turbopack) desde la 16.3. No existe en Next.js 15. | Cada arranque de next dev vuelve a compilar desde cero; la única alternativa es dejar el servidor en marcha el máximo tiempo posible. |
| 6.4 Logs del navegador en el terminal | 🆕 16.2 | La opción logging.browserToTerminal reenvía la consola del navegador al terminal de next dev; por defecto reenvía avisos y errores. En Next.js 15.4 y 15.5 existe como opción experimental con otro nombre. | Escribe experimental: { browserDebugInfoInTerminal: true } en next.config.ts; con versiones anteriores a la 15.4 tendrás que mirar la consola del navegador. |
6.5 La carpeta .next/dev | 🔄 16.0 | next dev escribe ahora en .next/dev, separado de la salida de next build en .next; en Next.js 15 ambos comparten .next. | Para el servidor de desarrollo antes de ejecutar next build en el mismo proyecto, porque los dos escriben en la misma carpeta y pueden estropearse mutuamente los ficheros. |
| 7.2 La tabla de rutas sin tamaños | 🔄 16.0 | next build ya no muestra el tamaño de cada ruta ni el «First Load JS», y tampoco ejecuta ESLint durante la compilación. | Verás esas columnas y la fase «Linting and checking validity of types»; en Next.js 16 usa next analyze (o next build --analyze) para conocer los tamaños y ejecuta npm run lint por separado. |
| 7.3 Tu primer error de compilación con Cache Components | 🔄 16.0 | Con cacheComponents activado, next build falla si una página lee datos de la petición (params, searchParams, cookies()…) o datos sin caché fuera de un <Suspense>. En Next.js 15 esa misma página compila sin error como ruta dinámica (ƒ). | El código sin <Suspense> funciona tal cual; añadir <Suspense> es igualmente buena práctica, porque permite enviar antes el resto de la página. |
| 9. ESLint con flat config | 🔄 16.0 | El comando next lint se ha eliminado, next build ya no ejecuta ESLint y la opción eslint de next.config deja de tener efecto. En Next.js 15.5, next lint sigue funcionando, aunque marcado como obsoleto, y next build ejecuta el linter. | Puedes seguir con next lint, pero es mejor migrar ya al CLI de ESLint con npx @next/codemod@canary next-lint-to-eslint-cli .; así el paso a 16 no te obligará a tocar nada. |
10.1 AGENTS.md y la documentación local | 🆕 16.2 | El paquete next incluye su documentación en node_modules/next/dist/docs/ y create-next-app genera AGENTS.md; desde la 16.3, next dev mantiene ese fichero actualizado (opción agentRules). No existe en Next.js 15. | Ejecuta npx @next/codemod@canary agents-md, que descarga una copia de la documentación de tu versión en .next-docs/ y crea el índice en AGENTS.md. |
| 10.3 El servidor MCP de desarrollo | 🆕 16.0 | next dev expone un servidor MCP en /_next/mcp con información del proyecto en marcha. En Next.js 15 esa ruta no existe y responde 404. | No hay alternativa equivalente; el asistente tendrá que leer el terminal y ejecutar next build para encontrar errores. |
02 - Modelos de renderizado
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
| Todo el módulo | 🔄 16.0 | Con cacheComponents (activado por create-next-app 16.4), PPR es el único modelo de las páginas del App Router: cada ruta se clasifica como ○, ◐ o ƒ, y el build falla si una página no puede producir un armazón estático sin decirlo. En Next.js 15 cada ruta es estática o dinámica entera, y PPR solo existe en las versiones canary. | Los apartados 3 a 7 te sirven con el código alternativo que se da en cada uno; en lugar de PPR usarás SSR con <Suspense> (apartado 8), y en la tabla de rutas verás ○, ● y ƒ. |
| El error que te encontrarás primero | 🔄 16.0 | Con cacheComponents, una página que espera datos sin caché o datos de la petición fuera de <Suspense> hace fallar next build. En Next.js 15 el build no se queja nunca: con connection(), cookies() o headers() la ruta pasa a ser dinámica (ƒ) en silencio, y con un fetch sin caché y nada más, se prerenderiza con el dato congelado. | No hay error que atender; usa connection() o export const dynamic = 'force-dynamic' para que la ruta sea SSR, y revisa la tabla del build para no congelar datos sin querer. |
SSR clásico con instant = false | 🆕 16.3 | La exportación instant no existe en Next.js 15. | No hace falta nada: la llamada a connection() (o a cookies()/headers()) ya convierte la ruta en dinámica, sin error de build. |
| 8. Partial Prerendering: armazón estático y huecos dinámicos | 🆕 16.0 | PPR estable llega con cacheComponents. En Next.js 15 solo existe como experimental.ppr, y en la versión estable 15.5.28 activarlo lanza The experimental feature "experimental.ppr" can only be enabled when using the latest canary version of Next.js. | Usa SSR con streaming (la misma página con <Suspense>); el usuario ve el fallback casi igual de pronto, pero el armazón se genera en cada petición en lugar de servirse ya hecho. |
| Símbolos de la tabla de rutas | 🔄 16.0 | Con cacheComponents, la tabla usa ○, ◐ y ƒ, y ya no muestra los tamaños (Size y First Load JS). En Next.js 15 usa ○, ● y ƒ, con tamaños. | Lee ● como “estática generada a partir de generateStaticParams”; no tendrás ◐. Para medir el JavaScript en 16, usa el analizador de paquetes del módulo 20. |
03 - App Router: páginas y layouts
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
| 3.5. Layouts que piden datos | 🔄 16.0 | En Next.js 15 no existe cacheComponents: un fetch sin opciones en un layout compila sin error, se ejecuta al compilar y la ruta queda estática (○), con los datos congelados hasta el siguiente build. | Para tener datos actuales, llama a await connection() (de next/server) al principio de Resumen y mantén el <Suspense> del ejemplo; la ruta pasará a ser dinámica (ƒ). |
5.5. transitionTypes | 🆕 16.2 | No existe en Next.js 15. | Quita la prop y la navegación funcionará igual, pero sin poder elegir una animación distinta según el enlace. No hay equivalente en <Link>. |
| 8. Precarga y navegación: qué pasa por la red | 🔄 16.0 | En Next.js 15 la precarga pide cada ruta entera en una sola petición, y los layouts compartidos se descargan una vez por enlace. | No tienes que cambiar nada en tu código; <Link> precarga igual, solo que con menos peticiones y más grandes. |
| 8.4. Partial Prefetching | 🆕 16.3 | No existe en Next.js 15. | La precarga por defecto de una ruta dinámica llega hasta su loading.tsx más cercano; añade loading.tsx a tus rutas dinámicas, y usa prefetch={true} en los enlaces donde quieras precargar la ruta completa. |
| 9. El estado de las páginas al navegar | 🆕 16.0 | No existe en Next.js 15: al salir de una página, sus componentes se desmontan y su estado se pierde. | Sube el estado que quieras conservar a un layout compartido, a la URL (searchParams) o a un almacén externo, como verás en el módulo 18. |
9.7. Reiniciar en cada visita nueva con bfcacheId | 🆕 16.3 | No existe en Next.js 15. | No lo necesitas, porque en Next.js 15 la página siempre se monta de cero al volver a ella, tanto con un enlace como con Atrás. |
04 - Rutas dinámicas y organización
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
3. params es una promesa | 🔄 16.0 | En Next.js 15 params y searchParams ya eran promesas, pero se podían leer de forma síncrona (params.id) por compatibilidad; en 16 ese acceso síncrono desaparece. | Escribe await params exactamente igual que aquí; el código de este módulo compila y funciona sin cambios en 15.5. |
5.2. searchParams con Cache Components | 🔄 16.0 | En Next.js 15 leer searchParams en cualquier sitio de la página la convierte entera en dinámica (ƒ en la tabla del build) y no exige <Suspense>. Con Cache Components, en 16, leerlo fuera de <Suspense> hace fallar el build. | Puedes hacer await searchParams directamente en la página; el <Suspense> de este ejemplo también funciona en 15 y no estorba. |
| 7.5. Rutas dinámicas con Cache Components | 🔄 16.0 | Con cacheComponents: true (lo que genera create-next-app en 16), generateStaticParams no puede devolver una lista vacía, la opción dynamicParams no existe y una página que lee params sin generateStaticParams necesita <Suspense>. | No hay Cache Components; puedes devolver [] para generarlo todo bajo demanda, usar export const dynamicParams = false para que lo no listado dé 404, y leer params sin <Suspense>. |
05 - Server y Client Components
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
| 4.5. El estado de los Client Components al navegar | 🔄 16.0 | Con cacheComponents activado (lo trae create-next-app desde 16.4), Next oculta con <Activity> las páginas que abandonas en lugar de desmontarlas (hasta 3), y al volver con <Link> sus Client Components conservan el estado. En Next.js 15 las páginas se desmontan y el estado se pierde. | Guarda en la URL (searchParams) o en un contexto del layout el estado que deba sobrevivir a la navegación. |
5.3. Datos sin caché y <Suspense> | 🆕 16.0 | Con la opción cacheComponents (activada por create-next-app 16.4), leer datos sin caché fuera de un <Suspense> hace fallar el build. No existe en Next.js 15. | El <Suspense> no es obligatorio; sin él la página compila como estática (○) con los datos congelados del build. El código de este apartado, con <Suspense>, compila igual en Next.js 15. |
06 - Estilos
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
2.4. scroll-behavior: smooth | 🔄 16.0 | En Next.js 15, si ponías scroll-behavior: smooth en html, Next lo desactivaba durante cada navegación para que el salto al principio de la página fuera instantáneo; en 16 ya no lo toca y la navegación también se desplaza suavemente. | No tienes que hacer nada, ese es el comportamiento por defecto; en 16, añade data-scroll-behavior="smooth" a la etiqueta <html> para recuperarlo. |
| 3.3. Nombres más cortos en producción | 🔄 16.4 | En Next.js 15 los nombres generados en producción son largos, como TarjetaProducto-module__KxBoHG__tarjeta; desde 16.4 Turbopack los acorta a Poillq_tarjeta, y el HTML y el CSS pesan menos. | No hay nada que hacer ni que cambiar en tu código; solo pierdes ese ahorro de bytes, y los nombres siguen siendo únicos. |
| 4. Tailwind CSS 4 con Turbopack | 🔄 16.4 | Hasta 16.3, y en Next.js 15, create-next-app integra Tailwind con PostCSS (@tailwindcss/postcss y postcss.config.mjs); desde 16.4 usa un cargador de Turbopack (@tailwindcss/turbopack) configurado en next.config.ts. | Sigue con PostCSS, que es lo que trae tu plantilla y funciona con CSS Modules sin cambios; el resto del apartado (clases, @theme, @reference) es igual. |
5.4. Importar Sass de node_modules | 🔄 16.0 | Turbopack pasa a ser el bundler por defecto, y no admite el prefijo ~ para importar de node_modules (@import '~bootstrap/...') que aceptaba webpack. | Con webpack (next dev sin --turbopack) el ~ sigue funcionando; con --turbopack, que es lo que trae la plantilla de 15.5, tienes el mismo límite que en 16. |
10.3. El modo 'graph' | 🆕 16.3 | No existe en Next.js 15. | Quédate con el valor por defecto (true), o usa 'strict' con webpack si tienes problemas de orden; no hay forma de ajustar el reparto entre bytes y peticiones. |
07 - Imágenes, fuentes y metadatos
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
6.2. preload: la imagen del LCP | 🆕 16.0 | No existe en Next.js 15: allí TypeScript la rechaza (Type '{ ...; preload: true; }' is not assignable...). | Usa priority, que hace lo mismo: añade un <link rel="preload"> en el <head> y quita la carga perezosa. |
6.3. priority, obsoleta en Next.js 16 | 🔄 16.0 | En Next.js 15, priority es la forma de precargar una imagen. En la 16 sigue funcionando, pero está marcada como obsoleta (@deprecated Use preload prop instead) en favor de preload. | Sigue usando priority; al migrar, cámbiala por preload. |
| 7.3. Imágenes de servidores de tu red: el bloqueo de IP locales | 🔄 16.0 | En Next.js 15 el optimizador descarga imágenes de cualquier servidor que cumpla remotePatterns, aunque esté en localhost o en tu red privada. En la 16 rechaza las IP privadas y responde 400, salvo que actives images.dangerouslyAllowLocalIP. | No tienes esa protección ni la opción para desactivarla; limita remotePatterns al máximo para no exponer servicios internos. |
| 7.4. Redirecciones: como mucho tres | 🔄 16.0 | En Next.js 15 el optimizador sigue todas las redirecciones del servidor remoto. En la 16 sigue como máximo 3 por defecto (opción maximumRedirects) y, si hay más, responde 508. | No hay límite ni opción para ponerlo; comprueba que tus URL remotas no encadenan redirecciones. |
8. Los cambios de next/image en Next.js 16 | 🔄 16.0 | Varios valores por defecto de images son más estrictos: calidades, anchos, caché, cadenas de consulta locales, IP privadas y redirecciones. | Funciona con los valores antiguos, más permisivos; puedes escribir a mano en next.config.ts la configuración de la sección 8.6 para que se comporte como la 16 y la migración no te dé sorpresas. |
| 8.1. Calidades permitidas: solo 75 | 🔄 16.0 | En Next.js 15, si no defines images.qualities, se acepta cualquier calidad de 1 a 100. En la 16 el valor por defecto es [75]: la prop quality se redondea al valor permitido más cercano y una petición directa con otra q da 400. | Funciona sin configurar nada; si quieres el comportamiento de la 16, añade qualities: [75]. |
8.2. El ancho 16 desaparece de imageSizes | 🔄 16.0 | En Next.js 15, imageSizes por defecto es [16, 32, 48, 64, 96, 128, 256, 384]; en la 16 se quita el 16. | Tienes el ancho 16 sin hacer nada. Al migrar, si necesitas iconos de 16 px exactos, declara imageSizes: [16, 32, 48, 64, 96, 128, 256, 384]. |
| 8.3. La caché de imágenes dura 4 horas | 🔄 16.0 | En Next.js 15, minimumCacheTTL vale 60 segundos; en la 16, 14 400 (4 horas). | Pon minimumCacheTTL: 14400 si quieres la misma duración; en la 16, baja a 60 si necesitas que los cambios de una imagen se vean antes. |
8.4. Imágenes locales con cadena de consulta: localPatterns | 🔄 16.0 | En Next.js 15 una imagen local con ? (por ejemplo /ofertas/banner.png?v=2) se optimiza sin más. En la 16 falla al renderizar y el optimizador responde 400, salvo que la permitas con images.localPatterns y su campo search. | Funciona sin configurar; localPatterns existe y puedes usarlo igual para restringir rutas. |
| 14.5. Una imagen por producto, con datos y fuente propia | 🔄 16.0 | En Next.js 15, la función de opengraph-image, twitter-image, icon y apple-icon recibe params como un objeto normal. En la 16 recibe una promesa (y id, si usas generateImageMetadata, también). | Escribe la firma como ({ params }: { params: { id: string } }) y usa params.id sin await. |
15.3. Directivas no estándar en robots | 🆕 16.3 | No existe en Next.js 15: el tipo de las reglas no tiene el campo other y TypeScript lo rechaza ('other' does not exist in type...). | Escribe el fichero estático app/robots.txt a mano con las directivas que necesites, en lugar de robots.ts. |
| 16.2. La capa de datos | 🆕 16.0 | La directiva 'use cache' es estable desde la 16; en Next.js 15 solo existe como experimento, tras la opción experimental.useCache, y sin ella no compila. | Borra las líneas 'use cache'; el proyecto compila igual y las páginas se prerenderizan en next build con los datos de ese momento (comprobado: el catálogo sale como ○ (Static) y las fichas como ● (SSG)). |
08 - Obtener datos en el servidor
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
3.1. Datos sin <Suspense>: el build falla | 🔄 16.0 | En Next.js 15 (sin cacheComponents) una página que hace fetch sin <Suspense> compila como estática: los datos se piden una vez en el build y se quedan congelados. | Añade await connection() (de next/server) antes de pedir los datos, o export const dynamic = 'force-dynamic', para que la página se genere en cada petición. |
3.3. Renunciar al armazón: instant = false | 🆕 16.3 | No existe en Next.js 15 (antes de 16.3 solo había una versión experimental, unstable_instant). | No hace falta; una página dinámica sin <Suspense> (por ejemplo, con await connection()) ya espera entera a los datos antes de responder. |
09 - Caché: Cache Components
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
| Todo el módulo | 🔄 16.0 | Todo este módulo gira en torno a Cache Components, que en Next.js 16 es estable y viene activado en los proyectos nuevos. En Next.js 15 estable no se puede activar: experimental.cacheComponents (antes dynamicIO) solo se admite en las versiones canary. | Usa el modelo de caché clásico, explicado entero y medido en el apartado 14, y la tabla de equivalencias del apartado 15. Con experimental.useCache: true puedes además usar 'use cache' en Next.js 15.5 (apartado 14.6). |
| 2. Activar Cache Components | 🆕 16.0 | No existe en Next.js 15 estable: experimental.cacheComponents solo se admite en canary. | No hay interruptor equivalente; la caché depende de las opciones de fetch y de segmento (apartado 14 de este módulo). |
3. 'use cache': guardar datos y componentes | 🔄 16.0 | En Next.js 15 'use cache' era experimental y se activaba con experimental.useCache: true, con unstable_cacheLife y unstable_cacheTag. En 16 es estable y se activa con cacheComponents. | Activa experimental.useCache (comprobado en 15.5.28, apartado 14.6) o usa unstable_cache y las opciones de fetch. |
4. cacheLife: cuánto vale un dato | 🔄 16.0 | En Next.js 15 se llamaba unstable_cacheLife, solo funcionaba con experimental.useCache, y el perfil 'seconds' tenía stale de 0 segundos (en 16 son 30). | Impórtala como unstable_cacheLife as cacheLife, o expresa la vida con next: { revalidate: segundos } en fetch o con la opción revalidate de unstable_cache. |
5. cacheTag: etiquetar para invalidar | 🔄 16.0 | En Next.js 15 se llamaba unstable_cacheTag y solo funcionaba con experimental.useCache. | Etiqueta los fetch con next: { tags: ['productos'] } o usa la opción tags de unstable_cache. |
6.2 updateTag: lee lo que acabas de escribir | 🆕 16.0 | No existe en Next.js 15. | Llama a revalidateTag('productos') (con un solo argumento) dentro de la Server Action; en 15.5 la respuesta de la acción ya trae el dato nuevo (medido en el apartado 14.5). |
6.3 revalidateTag: renovar sin hacer esperar a nadie | 🔄 16.0 | En Next.js 15 revalidateTag(tag) lleva un solo argumento y caduca la etiqueta en el acto. En 16 exige un segundo argumento, el perfil, y con 'max' sirve lo viejo mientras regenera. | Llama a revalidateTag('productos') sin segundo argumento; se comporta como updateTag o como revalidateTag(tag, { expire: 0 }) de la 16. |
6.4 refresh: repintar sin invalidar | 🆕 16.0 | No existe en Next.js 15. | Llama a router.refresh() desde un Client Component (con useRouter de next/navigation) o a revalidatePath con la ruta actual dentro de la acción. |
7.1 'use cache: remote' y 'use cache: private' | 🔄 16.0 | En Next.js 15 estas variantes solo existían como experimento junto a experimental.useCache. | Para datos de la sesión, lee la cookie fuera y pasa su valor como argumento a unstable_cache; para una caché compartida entre instancias, configura un cacheHandler para la Data Cache. |
9. Garantizar que una ruta siga estática: ensureStatic | 🆕 16.4 | No existe en Next.js 15. | export const dynamic = 'error' hace que el build falle si la ruta usa algo dinámico (comprobado: falla al leer cookies()), aunque solo cubre la ruta completa, no las etapas del prefetch. |
10. Etapas de la navegación: prefetch() y navigation() | 🆕 16.4 | No existen en Next.js 15, donde tampoco hay Partial Prefetching. | El prefetch de un <Link> trae la ruta hasta el primer loading.tsx (o entera con prefetch={true}); para que una parte pesada no se precargue, ponla tras un loading.tsx o cárgala en el cliente. |
10 - Server Actions y mutaciones
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
| 3.1. La capa de datos con etiquetas | 🔄 16.0 | En Next.js 15, 'use cache' y cacheTag solo existen como experimentales (experimental.useCache y unstable_cacheTag). | Etiqueta las lecturas con la opción de fetch next: { tags: ['departamentos'] } (y cache: 'force-cache'), e invalídalas en la acción con revalidateTag('departamentos'). Está comprobado con Next.js 15.5.28 (ejercicio 8). |
5.1. updateTag: lee lo que acabas de escribir | 🆕 16.0 | No existe en Next.js 15. | Llama a revalidateTag('departamentos') con un solo argumento, que en esa versión hace caducar la etiqueta al momento y la acción devuelve la página actualizada, con el mismo efecto (comprobado con Next.js 15.5.28, ejercicio 8). |
5.2. revalidateTag con perfil | 🔄 16.0 | En Next.js 15, revalidateTag(etiqueta) tiene un solo argumento y hace caducar la etiqueta al instante. En 16 exige un segundo argumento, el perfil de cacheLife ('max', 'hours'…), y pasa a ser stale-while-revalidate. | Llama a revalidateTag('departamentos') con un argumento; no hay forma de pedir el modo “sirve lo viejo mientras refrescas”. |
5.4. refresh | 🆕 16.0 | No existe en Next.js 15. | Llama a revalidatePath con la ruta actual dentro de la acción, o a router.refresh() desde el cliente después de que termine. |
11 - Formularios y validación
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
7. El enemigo de la mejora progresiva: <Suspense> | 🆕 16.0 | 'use cache', cacheLife, cacheTag y updateTag no existen de forma estable en Next.js 15. | No rodees el formulario con <Suspense> ni pongas un loading.tsx en su ruta; la página se renderiza entera en cada petición (ƒ), el HTML llega completo y funciona sin JavaScript. Tras guardar, usa revalidatePath en lugar de updateTag. |
12 - Route Handlers y APIs
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
8. Caché: cuándo un GET es estático | 🔄 16.0 | En Next.js 15, sin Cache Components, todo GET es dinámico salvo que lo marques, y no hay 'use cache', cacheLife ni cacheTag estables. | Añade al fichero export const dynamic = 'force-static' o export const revalidate = 3600, etiqueta los fetch con next: { tags: ['productos'] } (o usa unstable_cache) y llama a revalidateTag('productos') con un solo argumento (sección 8.6). |
13 - Proxy y configuración
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
| Todo el módulo | 🔄 16.0 | En Next.js 15 el fichero se llama middleware.ts, exporta una función middleware y se ejecuta por defecto en el runtime Edge. | Todo lo que hace proxy.ts en este módulo funciona igual en middleware.ts cambiando el nombre del fichero y de la función; si usas APIs de Node.js (node:crypto, process.version…), añade runtime: 'nodejs' a su config (estable desde 15.5). El apartado 3 lo compara paso a paso. Los apartados 6 y 7 (next.config y variables de entorno) funcionan igual en las dos versiones, salvo lo marcado en 6.4 y 7.6. |
2. proxy.ts: código antes de la ruta | 🔄 16.0 | En Next.js 15 esta función se llama middleware: fichero middleware.ts y función export function middleware(), con el runtime Edge por defecto. | Escribe el mismo código en middleware.ts con la función llamada middleware; la API (NextRequest, NextResponse, matcher) es la misma. |
3. De middleware.ts (Next.js 15) a proxy.ts (Next.js 16) | 🔄 16.0 | En Next.js 15 el fichero es middleware.ts, la función middleware y el runtime por defecto es Edge. | Renombra fichero y función en todos los ejemplos de este módulo, y añade runtime: 'nodejs' al config si el código usa APIs de Node.js; sin eso, el build pasa con un aviso, pero la ruta responde con un error 500. |
7.6. Ya no existen: serverRuntimeConfig y publicRuntimeConfig | 🔄 16.0 | Las opciones serverRuntimeConfig y publicRuntimeConfig de next.config se han eliminado. | Aún existen en sus tipos, pero no las uses en código nuevo: lee process.env en el servidor y usa NEXT_PUBLIC_ para el cliente, como en este módulo, y tu código funcionará igual en las dos versiones. |
14 - Errores y estados especiales
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
3.2. Qué props recibe error.tsx | 🔄 16.3 | En Next.js 15, error.tsx recibe solo error (tipado como Error & { digest?: string }) y reset, que no vuelve a pedir los datos al servidor. Next.js 16.2 añadió unstable_retry, y 16.3 lo estabilizó como retry. | Para reintentar de verdad, llama dentro de startTransition a router.refresh() y después a reset() (apartado 4.3). |
| 4.3. Reintentar de verdad en Next.js 15 | 🔄 16.3 | En Next.js 15 no existe retry y reset() no vuelve a pedir datos. | Combina router.refresh() (que pide de nuevo al servidor los Server Components de la ruta) con reset() dentro de un mismo startTransition. |
6. Límites de error dentro de una página: catchError | 🆕 16.3 | No existe en Next.js 15 (en 16.2 era unstable_catchError). | Escribe un límite de error de React como componente de clase con getDerivedStateFromError (apartado 6.3); funciona, pero no tiene retry() ni deja pasar por sí solo notFound() o redirect(). |
13.3. routeType: 'proxy' | 🔄 16.0 | En Next.js 15 los errores del middleware llegan a onRequestError con routeType: 'middleware'; en 16 el valor es 'proxy', igual que el fichero. | Compara con 'middleware' en lugar de 'proxy'; el resto de la API de instrumentation.ts es idéntica (es estable desde 15.0). |
15 - Rutas avanzadas y navegación instantánea
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
2.6. default.tsx es obligatorio | 🔄 16.0 | En Next.js 15, si un slot no tiene default.tsx, el proyecto compila y la recarga de esa URL muestra la página 404. | Crea igualmente los default.tsx (o un default.tsx que llame a notFound() si quieres conservar el 404); así el proyecto ya está listo para la 16. |
| 5.2. La página entera deja de responder a los clics | 🆕 16.0 | No existe en Next.js 15, donde la página que abandonas se desmonta y el <dialog> desaparece del documento. | No necesitas la limpieza del efecto, aunque no hace ningún daño y deja el componente listo para la 16. |
| 6. Navegación instantánea | 🆕 16.3 | No existe en Next.js 15: no hay validación de navegaciones ni opción instant (una página que la exporta compila, pero se ignora sin avisar). | Pon un loading.tsx en cada segmento al que se navega y un <Suspense> alrededor de cada dato lento, y revisa a mano, con la pestaña Red con limitación de velocidad, que cada navegación muestre algo al instante. |
| 7. Partial Prefetching | 🆕 16.3 | No existe en Next.js 15, donde cada <Link> visible precarga su propia URL. | En listas largas, pon prefetch={false} en los enlaces que rara vez se pulsan para no cargar el servidor con precargas, y deja la precarga por defecto en los enlaces importantes. |
| 8. View Transitions | 🔄 16.0 | En Next.js 15 las View Transitions de React son experimentales: hay que activar experimental.viewTransition: true en next.config.ts e importar unstable_ViewTransition y unstable_addTransitionType de react. | Activa esa opción, importa unstable_ViewTransition as ViewTransition, declara sus tipos (apartado 8.6) y marca la dirección con startTransition y router.push, porque transitionTypes de Link no existe. |
| 8.3. Dirección: adelante y atrás | 🆕 16.2 | No existe en Next.js 15: Link no tiene la prop transitionTypes. | Crea un enlace cliente que llame a startTransition(() => { addTransitionType('adelante'); router.push(href) }), con unstable_addTransitionType (apartado 8.6). |
16 - Base de datos con Drizzle
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
9.2. Caché de consultas con 'use cache' | 🆕 16.0 | 'use cache', cacheTag y cacheLife son estables desde la 16; en Next.js 15 solo existen como experimento (experimental.useCache, con unstable_cacheTag y unstable_cacheLife). | Envuelve la consulta con unstable_cache(consulta, ['catalogo'], { tags: ['productos'], revalidate: 3600 }) de next/cache (apartado 9.7); la página compila como ○ con Revalidate 1h. |
10.1. Cambiar el stock y renovar la caché con updateTag | 🆕 16.0 | updateTag no existe en Next.js 15. | Llama a revalidateTag('productos') (con un solo argumento, que en la 15 caduca la etiqueta al momento) en lugar de updateTag('productos'); el resto de la acción no cambia. |
10.3. Pedidos: datos sin caché y refresh | 🆕 16.0 | refresh() de next/cache no existe en Next.js 15. | Llama a revalidatePath('/pedidos') al final de la acción; vuelve a generar la página y el navegador recibe los datos nuevos igual. |
17 - Autenticación y autorización
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
| 7.2. Leer la sesión arriba de un layout rompe el build | 🔄 16.0 | En Next.js 15, leer cookies() arriba de un layout o una página, fuera de <Suspense>, compila sin error y hace que la ruta sea dinámica (ƒ). | Puedes escribir const usuario = await verificarSesion() directamente en el layout o la página; no necesitas <Suspense>, aunque sigue siendo buena idea para que el resto de la página no espere. |
| 10. El proxy como primera barrera | 🔄 16.0 | En Next.js 15 el fichero se llama middleware.ts y la función middleware; por defecto se ejecuta en el runtime Edge. | Crea middleware.ts con export async function middleware(request: NextRequest) y el mismo código; jose funciona en Edge. Importa jwtVerify de jose directamente en lugar de lib/sesion.ts, que usa next/headers y server-only. |
| 11. Autenticación y Cache Components | 🆕 16.0 | 'use cache', cacheTag, cacheLife y 'use cache: private' no existen en Next.js 15. | Lee la sesión con cookies() donde quieras (la ruta pasa a ser dinámica, ƒ) y guarda en caché los datos por usuario con unstable_cache(fn, ['clave'], { tags, revalidate }), pasando el identificador como argumento (apartado 11.4). No hay equivalente de 'use cache: private'. |
11.3. 'use cache: private': caché en el navegador de cada usuario | 🆕 16.0 | No existe en Next.js 15. | No hay equivalente; cada navegación vuelve a pedir al servidor los datos que dependen de la sesión. |
18 - Estado en el cliente y en la URL
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
3. La URL como estado: searchParams en el servidor | 🔄 16.0 | En Next.js 15 searchParams ya es una promesa, pero todavía se puede leer de forma síncrona (con un aviso); en 16 solo se puede esperar con await o use. | Escribe el mismo código con await searchParams, que funciona igual; para guardar en caché las consultas usa el modelo de caché de Next.js 15 del módulo 09 (apartado 14) en lugar de 'use cache'. |
4.5 useSearchParams necesita <Suspense> | 🔄 16.0 | En Next.js 15 la regla es la misma (una página estática que usa useSearchParams sin <Suspense> no compila), pero el error se llama “Missing Suspense boundary with useSearchParams” y, en páginas dinámicas, no se exige. | Envuelve igualmente el componente en <Suspense>; el arreglo es idéntico y no hay nada más que cambiar. |
5. El estado local y <Activity> al navegar | 🆕 16.0 | No existe en Next.js 15: al salir de una ruta, sus componentes se desmontan y su useState se pierde. | Guarda en la URL (apartado 3) o en un almacén global (apartados 6 y 7) el estado que deba sobrevivir a una navegación. |
8.6 Cache Components y la hora: el error de Date.now() | 🔄 16.0 | Con Cache Components, Next no permite leer la hora actual mientras prerenderiza, y TanStack Query la lee al guardar una consulta. En Next.js 15 no existe esa restricción: el dehydrate estándar compila en una página estática (se comprobó: ○ /hidratado con revalidate de 5 minutos). | Usa el patrón del apartado 8.4 sin connection() y sin el ayudante del apartado 8.7. |
| 8.8 Mutaciones: dos cachés que coordinar | 🆕 16.0 | updateTag no existe en Next.js 15. | Llama a revalidateTag('pedidos') (con un solo argumento) en la Server Action; el resto del patrón (useMutation e invalidateQueries) es igual. |
19 - Internacionalización
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
2.2. Leer el idioma con next/root-params | 🆕 16.3 | No existe en Next.js 15 (el código de 15.5 solo contiene un esbozo experimental, ligado a experimental.rootParams; no lo uses). | Lee el idioma de params en cada layout y página (const { lang } = await params) y pásalo como argumento a las funciones que lo necesiten. |
3.3. i18n/request.ts: la configuración de cada petición | 🆕 16.3 | No existe en Next.js 15: la lectura del idioma con next/root-params es de 16.3. | Lee el idioma de requestLocale en este fichero y llama a setRequestLocale(locale) en cada layout y página antes de traducir, como se explica en el apartado 12. |
3.6. proxy.ts: detectar y redirigir | 🔄 16.0 | En Next.js 15 este fichero se llama middleware.ts y puede ejecutarse en el runtime edge. | Guarda el mismo código en middleware.ts; createMiddleware funciona igual en los dos. |
12.1. request.ts con requestLocale | 🆕 16.3 | No existe en Next.js 15: next/root-params no está disponible de forma estable. | Usa requestLocale en request.ts y llama a setRequestLocale(locale) al principio de cada layout y página con params, como en el código de este apartado. |
12.2. middleware.ts en lugar de proxy.ts | 🔄 16.0 | En Next.js 15 el fichero se llama middleware.ts. | Pon el mismo contenido del apartado 3.6 en middleware.ts; createMiddleware(routing) y el matcher no cambian. |
20 - Rendimiento
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
5.4. priority ahora es preload | 🔄 16.0 | En Next.js 15 la propiedad para precargar la imagen del LCP es priority; en 16 se llama preload y priority queda obsoleta. | Escribe <Image priority ... />, que hace lo mismo (añade el <link rel="preload"> y quita la carga diferida). |
| 8. El analizador de paquetes de Turbopack | 🆕 16.1 | No existe en Next.js 15. | Compila con webpack (next build sin --turbopack) y usa el plugin @next/bundle-analyzer, que genera tres informes HTML en .next/analyze/ (apartado 8.5). |
| 8.2. Guardar el análisis y compararlo | 🆕 16.4 | next analyze export y los snapshots con nombre no existen en 15 ni en 16.0 a 16.3. | Guarda los HTML de @next/bundle-analyzer de cada versión y compáralos a mano; si usas 16.1 a 16.3, copia la carpeta .next/diagnostics/analyze con otro nombre antes de volver a analizar. |
8.6. next build ya no muestra tamaños | 🔄 16.0 | En Next.js 15, next build muestra una tabla con Size y First Load JS de cada ruta; en 16 esas columnas desaparecen. | Tienes esas cifras en la salida del build, pero tómalas como orientativas; para saber qué hay dentro, usa @next/bundle-analyzer. |
| 10. El React Compiler en Next.js | 🔄 16.0 | En Next.js 15 el React Compiler se activa con experimental.reactCompiler; en 16 pasa a ser estable y se configura con reactCompiler en la raíz de la configuración. | Instala babel-plugin-react-compiler y escribe experimental: { reactCompiler: true } en next.config.ts; el resultado es el mismo. |
| 10.3. La versión nativa en Rust | 🆕 16.3 | No existe en Next.js 15. | Usa la versión Babel con experimental.reactCompiler; no hay versión nativa. |
| 11.1. Caché en disco para desarrollo | 🆕 16.1 | En Next.js 15 la caché persistente de Turbopack era un experimento solo en las versiones canary: cada next dev empieza de cero. | No hay alternativa en la versión estable; mantén el servidor de desarrollo abierto en lugar de reiniciarlo. |
11.2. Caché en disco para build | 🔄 16.3 | Desde 16.3, next build guarda su trabajo en .next/cache/turbopack y el siguiente build parte de él; antes, cada build empezaba de cero. En Next.js 15 no existe. | No hay alternativa; cada next build --turbopack compila todo desde cero. |
21 - Testing
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
7.3. Si usas Next.js 15: revalidateTag en lugar de updateTag | 🆕 16.0 | updateTag no existe en Next.js 15. | La acción llama a revalidateTag('departamentos') (con un solo argumento) y la prueba comprueba expect(revalidateTag).toHaveBeenCalledWith('departamentos'); la simulación de next/cache del apartado 2.3 ya incluye revalidateTag. |
8.2. Si usas Next.js 15: unstable_cache | 🔄 16.0 | En Next.js 15 sin cacheComponents, la capa de datos guarda en caché con unstable_cache(función, claves, { tags }) en lugar de 'use cache' y cacheTag. | Simula next/cache con unstable_cache: (fn) => fn, que devuelve la función sin caché, y comprueba las etiquetas en las opciones del tercer argumento si lo necesitas. |
9.2. proxy.ts: el matcher y la función | 🔄 16.0 | En Next.js 15 el fichero se llama middleware.ts y la función exportada middleware. | Importa { config, middleware } desde @/middleware y usa exactamente las mismas utilidades de prueba (unstable_doesMiddlewareMatch, getRedirectUrl, isRewrite), que existen desde Next.js 15.1. |
10.3. next dev y next build a la vez | 🔄 16.0 | En Next.js 15, next dev y next build escriben en la misma carpeta .next, así que lanzar las E2E (que hacen next build) con next dev abierto en el mismo proyecto puede corromper la compilación de ambos. | Para next dev antes de ejecutar npm run test:e2e, y vuelve a arrancarlo cuando terminen. |
22 - Seguridad
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
4.6. Taint y 'use cache' | 🆕 16.0 | No existe en Next.js 15 (allí 'use cache' solo existe tras la opción experimental useCache). | Con unstable_cache, aplica el taint fuera de la función cacheada, sobre el resultado que devuelve, dentro de la función de la DAL que la llama; esa forma funciona igual en cualquier versión. |
7.2. El nonce en proxy.ts | 🔄 16.0 | En Next.js 15 el fichero se llama middleware.ts y la función exportada middleware. | Copia el mismo código en middleware.ts y renombra la función; en el laboratorio de Next.js 15.5 la CSP se aplicó igual, la página hidrató y el script inyectado quedó bloqueado. |
| 7.4. Hacer dinámica toda la aplicación | 🆕 16.3 | No existe en Next.js 15 (no hay Cache Components ni la opción instant). | Basta con leer headers() en el layout raíz, sin instant: todas las rutas pasan a ƒ (Dynamic) y la CSP con nonce funciona; comprobado en el laboratorio de 15.5. |
| 7.7. Subresource Integrity con Turbopack | 🔄 16.2 | En Next.js 15 la opción experimental.sri existe, pero Turbopack no la aplica; según el blog de Next.js 16.2, Turbopack la admite desde esa versión. | Para probarla, compila con webpack (next build sin --turbopack). |
23 - Despliegue
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
| 5.2. La trampa: el HTML dice una cosa y la pantalla otra | 🔄 16.0 | En Next.js 15 no hay armazón estático con huecos: una página que lee process.env sin APIs dinámicas es estática entera (valor del build en HTML y RSC), y con connection() es dinámica entera. | No verás el cambio en pantalla, pero la regla es la misma: lee las variables de cada entorno después de await connection(), que existe también en la 15. |
| 6.2. Problema 1: cada réplica tiene su propia caché | 🔄 16.0 | El ejemplo usa 'use cache', cacheTag y cacheLife, que en Next.js 15 son experimentales. | El problema es idéntico con unstable_cache o fetch con next: { revalidate, tags }, cuya caché también vive en cada réplica, y revalidateTag('marca') con un solo argumento. |
| 6.3. La solución: un manejador de caché compartido | 🔄 16.0 | En Next.js 15, 'use cache' y la opción cacheHandlers son experimentales (experimental.cacheHandlers, junto con experimental.useCache). | Con el modelo de caché de la 15 (fetch con next.revalidate, ISR, unstable_cache) usa la opción estable cacheHandler (en singular), que cubre exactamente esas cachés y se comparte igual a través de Redis. |
| 7.1. La primera sorpresa: Cache Components no es compatible | 🔄 16.0 | En Next.js 15 la plantilla no activa Cache Components, así que output: 'export' funciona sin quitar nada. | Añade output: 'export' y sigue con la sección 7.2; el catálogo usa ya el modelo de datos de la 15. |
| 9. La Adapter API | 🆕 16.2 | No existe en Next.js 15 como API estable. | La 15.5 tiene una versión experimental en experimental.adapterPath, con una interfaz distinta y sin garantías; despliega con next start, con Docker standalone o con la integración propia de cada proveedor. |
24 - Pages Router y migración
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
5.3 getStaticProps → 'use cache' | 🔄 16.0 | En Next.js 15 no existen 'use cache' ni cacheLife estables: un componente async que no lee datos de la petición es estático por defecto, y la caché se controla con export const revalidate y las opciones de fetch. | Quita 'use cache', cacheLife y cacheTag de lib/datos.ts y usa fetch(url, { cache: 'force-cache', next: { revalidate: 60, tags: ['estadisticas'] } }), o export const revalidate = 60 en la página. |
6.2 La lista de empleados: SSR con <Suspense> | 🔄 16.0 | Con Cache Components, leer searchParams o pedir datos sin caché fuera de <Suspense> hace fallar el build; la página queda como ◐ (armazón estático y hueco dinámico). En Next.js 15 la página se vuelve dinámica entera (ƒ) en cuanto lee searchParams, sin necesidad de <Suspense>. | Puedes hacer await searchParams directamente en la página y pedir los datos ahí; <Suspense> es opcional y solo sirve para mostrar el mensaje de carga mientras llegan. |
6.4 proxy.ts | 🔄 16.0 | En Next.js 15 el fichero se llama middleware.ts y la función exportada middleware; en 16 la convención pasa a proxy.ts con la función proxy, que siempre usa el runtime de Node. | Llama al fichero middleware.ts y a la función middleware; el cuerpo es el mismo, incluida la copia del valor anterior en una cabecera. |
| 6.5 Las API Routes → Route Handlers | 🔄 16.0 | revalidateTag exige un segundo argumento con el perfil de cacheLife ('max' para servir lo antiguo mientras se regenera); en Next.js 15 se llamaba con un solo argumento. | Escribe revalidateTag('estadisticas') y, si la página usa export const revalidate, también puedes usar revalidatePath('/'). |
| 12. Paso 3: el codemod de APIs asíncronas | 🔄 16.0 | Next.js 15 aceptaba params, searchParams, cookies(), headers() y draftMode() de forma síncrona por compatibilidad (con avisos); Next.js 16 solo las admite con await. | El mismo codemod (npx @next/codemod@latest next-async-request-api .) ya funciona en 15; aplícalo allí antes de actualizar para que la versión 16 no te sorprenda. |
13.1 next lint ya no existe | 🔄 16.0 | next lint se ha eliminado y next build ya no ejecuta el linter. En Next.js 15 next lint funcionaba (con aviso de obsoleto) y next build lo ejecutaba en la fase “Linting and checking validity of types”. | Puedes seguir con next lint, pero mejor migra ya a eslint . con npx @next/codemod@canary next-lint-to-eslint-cli ., que también funciona en 15. |
13.2 Rutas paralelas sin default.tsx | 🔄 16.0 | Todos los slots de rutas paralelas necesitan un default.tsx para las URL en las que no tienen página; sin él, next build falla. En Next.js 15 compilaba y la URL respondía 404 al abrirla directamente. | Añade igualmente default.tsx a cada slot; arregla ese 404 y te ahorra el error al actualizar. |
13.3 Errores de TypeScript: revalidateTag y UnsafeUnwrappedCookies | 🔄 16.0 | revalidateTag exige un segundo argumento con un perfil de cacheLife ('max' en casi todos los casos), y desaparecen los tipos UnsafeUnwrapped* de next/headers. En Next.js 15 revalidateTag('productos') era correcto y los tipos existían. | Sigue con revalidateTag('productos'); y cambia ya los casts UnsafeUnwrappedCookies por await cookies(), que funciona igual en 15. |
13.4 next/image: la calidad 90 ya no está permitida | 🔄 16.0 | images.qualities vale por defecto [75]: cualquier otra calidad se redondea a 75 en el HTML y, pedida directamente, da 400. En Next.js 15 sin qualities se aceptaba cualquier valor de 1 a 100. | No necesitas nada; si quieres limitar las calidades (y evitar que te pidan miles de variantes), declara ya images: { qualities: [75, 90] }. |
| 14. Paso 5: migrar a Cache Components | 🆕 16.0 | cacheComponents no existe en Next.js 15 (allí había un experimento distinto, experimental.dynamicIO, que 16 elimina). | Quédate con export const revalidate, dynamic y las opciones de fetch (cache, next.revalidate, next.tags); la migración de esta sección se hace después de actualizar a 16. |
25 - Proyecto final
| Apartado | Versión | Qué ocurre | Si usas Next.js 15 |
|---|---|---|---|
| Todo el módulo | 🔄 16.0 | El proyecto se apoya en Cache Components, updateTag, proxy.ts y ensureStatic, que no existen en Next.js 15. | El apartado 11 recoge, comprobado en Next.js 15.5.28, cada cambio (unstable_cache, revalidateTag, middleware.ts, reset) y lo que se pierde: el armazón estático de la zona de gestión. |
5.2. Una garantía para toda la tienda: ensureStatic | 🆕 16.4 | No existe en Next.js 15. | No hay equivalente por etapas; export const dynamic = 'error' en la portada hace que el build falle si se vuelve dinámica, pero no sirve para la ficha, que tiene un hueco dinámico. Comprueba a mano la tabla del build en cada cambio. |
retry en error.tsx | 🆕 16.3 | La propiedad retry (que vuelve a pedir los datos y a renderizar) apareció como unstable_retry en 16.2 y es estable desde 16.3. | Recibe reset en su lugar ({ error, reset }), que vuelve a renderizar el segmento sin volver a pedir los datos al servidor; si el fallo venía de los datos, combínalo con router.refresh() dentro de startTransition. |
| 11. Si usas Next.js 15 | 🔄 16.0 | El proyecto depende de Cache Components ('use cache', cacheTag, updateTag, ensureStatic), de proxy.ts y de retry en error.tsx, que no existen en Next.js 15. | Cambia 'use cache' por unstable_cache, updateTag por revalidateTag con un argumento, proxy.ts por middleware.ts con jose directo, retry por reset, y quita ensureStatic, cacheComponents y partialPrefetching; el resto del código (DAL, acciones, Zod, Drizzle, pruebas) no cambia. |