Módulo 00: Introducción a Next.js
Objetivos del módulo
- Entender qué es Next.js y en qué se diferencia de React, que es una biblioteca y no un framework
- Saber qué problemas de una aplicación React “pelada” resuelve Next.js y cuáles no
- Ver una primera página de Next.js funcionando y leer el HTML que llega de verdad al navegador
- Recorrer un proyecto recién creado con
create-next-appy saber para qué sirve cada fichero - Distinguir las tres órdenes del ciclo de trabajo (
next dev,next buildynext start) y lo que hace cada una - Seguir una petición completa: armazón prerenderizado, streaming, carga útil RSC e hidratación
- Medir de verdad una página de Next.js frente a la misma página hecha como SPA con Vite, e interpretar los números sin prejuicios
- Conocer la historia del proyecto, de dónde viene el App Router y qué papel juega Vercel
- Comparar Next.js con React + Vite, React Router (antes Remix), Astro y Nuxt, y saber cuándo elegir cada uno
- Entender la política de versiones y soporte (LTS) de Next.js, el programa mensual de parches de seguridad y por qué hay que instalar siempre el último parche
- Decidir con criterio entre Next.js 16 y Next.js 15 para un proyecto concreto
- Saber leer las marcas 🆕 y 🔄 que usa este curso y tener un mapa de lo que vas a aprender
💡 Consejo: Este módulo casi no te pide escribir código, pero no te lo saltes. Next.js ha cambiado tanto entre versiones que gran parte de lo que encontrarás en tutoriales, foros y respuestas de asistentes de IA está desfasado. Saber qué versión describe cada fuente y qué modelo usa (Pages Router, App Router con la caché de Next.js 15, o Cache Components) te ahorrará muchas horas de depuración.
📘 Concepto: Este curso usa Next.js 16.4, con React 19.3, TypeScript 5 y Tailwind CSS 4. Todos los ejemplos y todas las salidas que verás se han obtenido con Next.js 16.4.3. Cuando lo instales tendrás seguramente un parche posterior (16.4.4 salió el 2 de noviembre de 2026, y habrá más): instala siempre el último 16.4.x. Los parches corrigen fallos y vulnerabilidades sin cambiar las APIs, así que todo lo que leas aquí funcionará igual. El apartado 9.3 explica por qué es importante.
1. Qué es Next.js
Next.js es un framework de React para crear aplicaciones web completas: la parte que ve el usuario y una parte de servidor que vive en el mismo proyecto. Lo desarrolla Vercel como proyecto de código abierto con licencia MIT.
Para entender qué significa eso hay que separar dos palabras que se confunden mucho:
- Una biblioteca es código que tú llamas cuando lo necesitas. React es una biblioteca: te da componentes, estado y hooks para describir una interfaz, y no opina sobre nada más. No sabe qué es una URL, no sabe compilar tu código, no sabe dónde vive tu servidor.
- Un framework es código que te llama a ti. Tú escribes piezas siguiendo sus convenciones (un fichero
page.tsxen tal carpeta, una función exportada con tal nombre) y el framework decide cuándo ejecutarlas, en qué máquina y con qué datos. Es el principio de “inversión de control”.
Cuando trabajaste con React y Vite (por ejemplo en el Curso de React), tuviste que tomar tú todas las decisiones que no son de React: qué router usar, cómo pedir datos, dónde guardar el estado del servidor, cómo dividir el JavaScript en trozos, cómo poner el título de cada página. Next.js toma esas decisiones por ti y las integra entre sí.
Figura 1. React describe la interfaz. Next.js pone todo lo demás: rutas, servidor, compilación, caché y despliegue.
📘 Concepto: Un framework full stack reúne en un único proyecto el código de cliente (lo que se ejecuta en el navegador) y el de servidor (lo que se ejecuta en Node.js). En Next.js la frontera entre ambos no es una carpeta
frontend/y otrabackend/, sino una propiedad de cada componente y de cada función, como verás en el módulo 05.
1.1. Lo que Next.js añade a React
| Necesidad | Con React + Vite | Con Next.js |
|---|---|---|
| Rutas | Instalas y configuras React Router o TanStack Router | Las carpetas de app/ son las URL (módulo 03) |
| Generar HTML en el servidor | Montas tú un servidor con SSR, o no lo haces | Viene de serie, y además en el build o en streaming (módulo 02) |
| Pedir datos | useEffect o TanStack Query desde el navegador | await fetch(...) dentro del componente, en el servidor (módulo 08) |
| Guardar datos | Un backend aparte (Express, Spring…) y fetch | Server Actions y Route Handlers en el mismo proyecto (módulos 10 y 12) |
| Caché | La gestionas tú en el cliente | 'use cache', perfiles de duración e invalidación por etiquetas (módulo 09) |
| Imágenes y fuentes | Las optimizas a mano | next/image y next/font (módulos 06 y 07) |
| Título y metadatos por página | document.title o una biblioteca | Metadata API, sitemap, robots (módulo 07) |
| Compilación | Vite | Turbopack, integrado y sin configurar |
| Server Components | No disponibles | El modelo por defecto |
La última fila es la más importante. Los React Server Components (componentes que se ejecutan solo en el servidor) forman parte de React desde la versión 19, pero React solo define cómo funcionan: hace falta un servidor y un bundler que sepan separar el código de servidor del de cliente. Vite, por sí solo, no lo hace. Next.js fue el primer framework en ofrecerlos de forma estable, y hoy es la forma más directa de usarlos.
1.2. Lo que Next.js no hace por ti
Un framework no es magia. Next.js no incluye:
- Base de datos ni ORM. Usarás SQLite con Drizzle a partir del módulo 16.
- Sistema de autenticación completo. Lo construirás con cookies y
jose, y verás Better Auth en el módulo 17. - Biblioteca de componentes visuales. Usarás Tailwind CSS 4, que
create-next-appinstala por defecto. - Validación de formularios. Usarás Zod 4 en el módulo 11.
Lo que sí hace es dejar sitio para cada una de esas piezas y decirte dónde va cada una.
1.3. Lo que dice el propio equipo de React
La documentación oficial de React (react.dev, apartado Creating a React App) ya no recomienda empezar con una SPA hecha a mano. Recomienda usar un framework y cita, por este orden, Next.js (App Router), React Router (v7) y Expo (para aplicaciones nativas). Montar React desde cero con Vite, Parcel o Rsbuild queda como opción para quien tiene necesidades que no cubren los frameworks, quiere construir su propio framework o, simplemente, quiere aprender las bases.
Eso no convierte a Vite en algo malo. Una SPA sigue siendo la mejor elección para muchas aplicaciones internas detrás de un login, y en el apartado 6 verás con números que no siempre es más lenta. Pero explica por qué un curso de React moderno termina, tarde o temprano, en un framework.
2. Qué problemas resuelve
Para valorar Next.js hay que mirar primero qué le pasa a una SPA clásica hecha con React y Vite. Este es el index.html real que genera Vite para una aplicación React:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>lab-react</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Todo el contenido de la página, los menús, los textos, la lista de departamentos, se construye después, en el navegador, cuando ya se ha descargado y ejecutado el JavaScript. De ahí salen cinco problemas.
1. La pantalla en blanco. Hasta que el JavaScript llega y se ejecuta, el usuario no ve nada. En un portátil con fibra son décimas de segundo. En un móvil de gama baja con mala cobertura pueden ser varios segundos.
2. Las cascadas de peticiones (waterfalls). El navegador descarga el HTML, después el JavaScript, después React monta los componentes, y solo entonces los efectos piden los datos a la API. Si un componente hijo necesita los datos del padre para hacer su propia petición, se añade otro viaje. Cada paso espera al anterior.
3. Los buscadores y las vistas previas. Un buscador que lee el HTML sin ejecutar JavaScript ve un <div> vacío. Las redes sociales que generan una vista previa al pegar un enlace leen las etiquetas <meta> del HTML, y en una SPA son las mismas para todas las páginas.
4. Todo el código viaja al navegador. Si para pintar una tabla de productos usas una biblioteca de formato de fechas de 50 kB, esos 50 kB los descarga cada usuario, aunque la tabla nunca cambie después.
5. Los secretos no pueden estar en el cliente. Una clave de API o la cadena de conexión a una base de datos no pueden aparecer en código que se envía al navegador. Así que necesitas un backend aparte solo para hacer de intermediario.
Figura 2. En la SPA, el navegador hace todo el trabajo en varios viajes. En Next.js, el servidor hace el trabajo cerca de los datos y envía HTML ya construido.
Next.js ataca los cinco problemas con la misma idea: mover trabajo al servidor, o al momento de compilar, siempre que se pueda. El servidor está cerca de la base de datos, no tiene que descargar nada y puede guardar secretos. El navegador recibe HTML con contenido y solo el JavaScript de las partes interactivas.
⚠️ Importante: Mover trabajo al servidor tiene precio. Necesitas un servidor Node.js en marcha (o un proveedor que lo ponga por ti), cada petición dinámica consume CPU allí, y el modelo mental es más complejo: ahora cada componente vive en un lado de una frontera, y la caché pasa a ser algo que tienes que entender. Por eso este curso dedica módulos enteros a renderizado, a Server y Client Components y a la caché.
2.1. Tu primera página con Next.js
Veamos la idea en código. No te preocupes por instalar nada todavía: en el módulo 01 crearás el proyecto paso a paso. Aquí solo interesa qué hace cada pieza.
La página muestra los departamentos de la API EMPRESA (la misma API de los cursos de React y Angular) y un botón con un contador:
// app/page.tsx
import { Suspense } from 'react'
import { Contador } from './contador'
const API = process.env.API_URL ?? 'http://localhost:3000/api'
type Departamento = {
deptId: number
nombre: string
localidad: string
}
// Server Component asíncrono: se ejecuta solo en el servidor
async function ListaDepartamentos() {
const respuesta = await fetch(`${API}/departamentos`)
const datos: { contenido: Departamento[] } = await respuesta.json()
return (
<ul className="space-y-1">
{datos.contenido.map((d) => (
<li key={d.deptId} className="rounded bg-indigo-50 px-3 py-1">
<strong>{d.nombre}</strong> ({d.localidad})
</li>
))}
</ul>
)
}
export default function Inicio() {
return (
<main className="mx-auto max-w-xl space-y-6 p-8">
<h1 className="text-3xl font-bold text-indigo-900">EMPRESA</h1>
<p>Departamentos leídos en el servidor:</p>
<Suspense fallback={<p>Cargando departamentos...</p>}>
<ListaDepartamentos />
</Suspense>
<Contador />
</main>
)
}
// app/contador.tsx
'use client'
import { useState } from 'react'
// Client Component: se renderiza en el servidor y se hidrata en el navegador
export function Contador() {
const [visitas, setVisitas] = useState(0)
return (
<button
className="rounded bg-indigo-700 px-4 py-2 text-white"
onClick={() => setVisitas(visitas + 1)}
>
Me has pulsado {visitas} veces
</button>
)
}
Para que la página sea tuya, el layout raíz también se ha simplificado (en español y sin las fuentes de la plantilla):
// app/layout.tsx
import type { Metadata } from 'next'
import './globals.css'
export const metadata: Metadata = {
title: 'EMPRESA con Next.js',
description: 'Primera página del Curso de Next.js',
}
export default function RootLayout({ children }: LayoutProps<'/'>) {
return (
<html lang="es">
<body>{children}</body>
</html>
)
}
Fíjate en tres cosas que en una SPA serían imposibles o muy incómodas:
ListaDepartamentoses una funciónasyncque haceawait fetch(...)directamente, sinuseEffect, sin estado de carga y sinuseState. Puede hacerlo porque es un Server Component: se ejecuta en el servidor, espera los datos y devuelve el HTML ya rellenado.Contadorempieza por'use client'. Esa directiva marca la frontera: a partir de ahí el código sí viaja al navegador, porque necesitauseStateyonClick.- No hay ningún fichero de rutas. El fichero
app/page.tsxes la ruta/.

Figura 3. La página en marcha. La lista se generó en el servidor; el botón es lo único interactivo.
2.2. Qué llega de verdad al navegador
Lo interesante es el HTML. Si pides la página con curl (o con “Ver código fuente” en el navegador), aparecen, entre otras, estas líneas reales:
<h1 class="text-3xl font-bold text-indigo-900">EMPRESA</h1>
<template id="B:0"></template><p>Cargando departamentos...</p>
...
<div hidden id="S:0"><ul class="space-y-1"><li class="rounded bg-indigo-50 px-3 py-1"><strong>DIRECCIÓN</strong> (<!-- -->Madrid<!-- -->)</li>...
...
<button class="rounded bg-indigo-700 px-4 py-2 text-white">Me has pulsado <!-- -->0<!-- --> veces</button>
Léelas despacio, porque resumen medio curso:
- El
<h1>y el textoCargando departamentos...forman el armazón (shell). Next.js lo generó al compilar, porque no depende de nada que cambie entre peticiones. - La lista llega en la misma respuesta, un poco después, dentro de un
<div hidden id="S:0">. Un pequeño script en línea la mueve a su sitio, en lugar del texto de carga. Eso es streaming, y lo controla el<Suspense>que rodea aListaDepartamentos. - El botón ya viene en el HTML con su texto inicial (
0 veces). Cuando llega el JavaScript, React lo hidrata: le engancha elonClicksin volver a dibujarlo. - El código de
ListaDepartamentos(elfetch, la URL de la API, el tipoDepartamento) no aparece en ningún fichero JavaScript enviado al navegador. Solo viaja su resultado. En el apartado 5 lo comprobarás buscando dentro de los ficheros compilados.
Al compilar, Next.js resume todo esto en una línea:
Route (app)
┌ ◐ /
└ ○ /_not-found
○ (Static) prerendered as static content
◐ (Partial Prerender) prerendered as static HTML with dynamic server-streamed content
El símbolo ◐ significa prerenderizado parcial: una parte estática (el armazón) y una parte dinámica que se genera en cada petición y llega por streaming. Lo estudiarás a fondo en el módulo 02.
💡 Consejo: Acostúmbrate desde ya a mirar el HTML que devuelve el servidor y la lista de rutas de
next build. Son la forma más rápida de saber si una página es estática, dinámica o parcial, y casi todas las sorpresas de rendimiento se ven ahí antes que en el navegador.
3. Recorrido por un proyecto recién creado
Antes de modificar nada, merece la pena abrir un proyecto tal y como lo deja create-next-app y entender cada pieza. En el módulo 01 verás todas las opciones del asistente; aquí basta con saber que la orden
npx create-next-app@latest empresa-next --yes
crea un proyecto con las opciones recomendadas: App Router, TypeScript, Tailwind CSS 4, ESLint y Turbopack. Durante la instalación muestra qué paquetes añade:
Initializing project with template: app-tw
Installing dependencies:
- next
- react
- react-dom
Installing devDependencies:
- @tailwindcss/turbopack
- @types/node
- @types/react
- @types/react-dom
- eslint
- eslint-config-next
- tailwindcss
- typescript
Fíjate en lo poco que hay en dependencies: solo next, react y react-dom. Todo lo demás (TypeScript, Tailwind, ESLint, tipos) son herramientas de desarrollo que no llegan a producción.
3.1. Qué hay en la carpeta
empresa-next/
├── app/ Rutas de la aplicación (App Router)
│ ├── favicon.ico Icono de la pestaña: convención de fichero
│ ├── globals.css CSS global: importa Tailwind y define variables
│ ├── layout.tsx Layout raíz: <html> y <body> de todas las páginas
│ └── page.tsx La página de la ruta /
├── public/ Ficheros que se sirven tal cual (/next.svg, /vercel.svg...)
├── AGENTS.md Instrucciones para asistentes de IA
├── eslint.config.mjs Configuración de ESLint (formato "flat")
├── next-env.d.ts Tipos de Next.js para TypeScript (no se edita)
├── next.config.ts Configuración de Next.js
├── package.json Dependencias y scripts
└── tsconfig.json Configuración de TypeScript
Si vienes de Vite, echarás en falta tres cosas: no hay index.html (lo genera Next.js a partir del layout), no hay main.tsx con createRoot (Next.js hidrata la página por ti) y no hay src/ (el asistente lo ofrece como opción, pero por defecto todo cuelga de la raíz).
Al ejecutar npm run dev y abrir la página, ves la portada de la plantilla:

Figura 4. La página que genera create-next-app, servida por next dev. El círculo con la “N”, abajo a la izquierda, es el indicador de desarrollo: solo aparece con next dev.
3.2. app/layout.tsx y app/page.tsx
El layout raíz de la plantilla es este (los comentarios son del curso):
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
// next/font descarga la fuente al compilar y la sirve desde tu propio dominio
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
// Metadata API: Next.js convierte este objeto en <title> y <meta>
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
// LayoutProps<"/"> es un tipo global que genera Next.js para cada ruta
export default function RootLayout({ children }: LayoutProps<"/">) {
return (
<html
lang="en"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col">{children}</body>
</html>
);
}
Tres ideas que verás muchas veces:
- El layout raíz es obligatorio y es el único sitio donde se escriben
<html>y<body>. Todas las páginas se pintan dentro de suchildren. export const metadatasustituye a lo que en Vite escribías a mano enindex.html. Cada página puede exportar el suyo (módulo 07).LayoutProps<"/">no se importa de ningún sitio: es un tipo global que Next.js genera a partir de tus rutas. Por esotsconfig.jsonincluye las carpetas.next/typesy.next/dev/types.
page.tsx es un componente normal exportado por defecto. En la plantilla muestra la portada de la figura 4 con next/image. Lo sustituirás en cuanto empieces a trabajar.
3.3. next.config.ts
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
cacheComponents: true,
partialPrefetching: true,
turbopack: {
rules: {
"*.css": {
loaders: ["@tailwindcss/turbopack"],
as: "*.css",
},
},
},
};
export default nextConfig;
cacheComponents: trueactiva el modelo de caché de Next.js 16, que estudiarás en el módulo 09. Es lo que hace que una página con datos sin<Suspense>ni caché falle al compilar, como verás en el apartado 10.2.partialPrefetching: trueactiva la precarga parcial de rutas de la 16.3 (módulo 15).turbopack.rulesle dice a Turbopack que pase los ficheros CSS por el cargador de Tailwind. Es la forma en quecreate-next-appintegra Tailwind 4 con Turbopack.
3.4. package.json, tsconfig.json y eslint.config.mjs
Los scripts son cuatro, y los tres primeros son las órdenes del apartado 4:
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint"
}
lint llama directamente a ESLint: desde Next.js 16 ya no existe la orden next lint. La configuración de ESLint usa el formato flat e importa dos conjuntos de reglas de Next.js:
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";
const eslintConfig = defineConfig([
...nextVitals,
...nextTs,
globalIgnores([".next/**", "out/**", "build/**", "next-env.d.ts"]),
]);
export default eslintConfig;
En tsconfig.json hay dos detalles propios de Next.js: un plugin ("plugins": [{ "name": "next" }]) que añade avisos específicos en el editor, y el alias "@/*": ["./*"], que te permite escribir import { x } from '@/app/lib/datos' en lugar de rutas relativas largas.
3.5. AGENTS.md y la documentación local
AGENTS.md es un fichero de instrucciones para asistentes de programación con IA. Su contenido real es breve y muy revelador:
## This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all
differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/`
(resolved from this file's directory; ...) before writing any code. Heed deprecation notices.
El paquete next incluye toda su documentación en node_modules/next/dist/docs/, en Markdown, con la versión exacta que tienes instalada. Next.js reconoce así un problema real: los asistentes de IA se entrenaron con código de versiones antiguas y tienden a proponer APIs que ya no existen. El fichero les pide leer la documentación instalada antes de escribir código. Para ti también es útil: si dudas de si algo existe en tu versión, esa carpeta es la respuesta más fiable.
💡 Consejo:
next devvuelve a escribir ese bloque enAGENTS.mdsi lo borras (lo verás en su salida: “Generated AGENTS.md for AI agents”). Si no lo quieres, la propia salida indica la opción de configuración para desactivarlo. Lo normal es dejarlo y subirlo al repositorio.
4. El ciclo de trabajo: next dev, next build y next start
En Vite tenías vite (desarrollo), vite build (compilar) y vite preview (probar lo compilado). Next.js tiene tres órdenes equivalentes, pero la tercera no es una simple vista previa: es el servidor de producción.
Figura 5. Las tres órdenes. dev es para programar, build prepara todo lo posible por adelantado y start sirve el resultado.
4.1. next dev: programar
Esta es la salida real de next dev en el proyecto recién creado, con dos peticiones a /:
▲ Next.js 16.4.3 (Turbopack)
- Local: http://127.0.0.1:5500
- Network: http://127.0.0.1:5500
✓ Ready in 26.6s
✓ Running next.config.ts took 511ms
- Cache Components enabled
- Partial Prefetching enabled
✓ Generated AGENTS.md for AI agents. Set `agentRules: false` in next.config to disable.
○ Compiling / ...
GET / 200 in 6.4s (next.js: 5.0s, application-code: 1404ms)
GET / 200 in 534ms (next.js: 92ms, application-code: 442ms)
(En el curso ves el puerto 5500 porque los ejemplos se probaron así; tú usarás el 3001, como se explica en el apartado 12.2.)
Lo que cuenta esta salida:
- Turbopack compila bajo demanda. “Ready” no significa que todo esté compilado: la ruta
/se compiló al pedirla por primera vez (Compiling / ...). En un proyecto con 50 rutas, solo se compilan las que visitas. - La primera petición es lenta y las siguientes, no. 6,4 s la primera (casi todo trabajo de Next.js: compilar), 534 ms la segunda. Desde Next.js 16.1, Turbopack guarda en disco lo compilado, así que el siguiente arranque será más rápido.
- Cada línea separa el tiempo de Next.js del de tu código (
next.jsfrente aapplication-code). Si una página va lenta en desarrollo, esta línea te dice a quién culpar. - Escribe en
.next/dev, no en.next. Desde Next.js 16, desarrollo y compilación usan carpetas distintas, de modo que puedes tenernext deven marcha mientras ejecutasnext build.
Los tiempos absolutos dependen mucho del equipo (estas cifras se tomaron en un equipo con otros procesos compitiendo por la CPU); fíjate en las proporciones.
⚠️ Importante: En modo desarrollo Next.js renderiza todo en cada petición, añade código de depuración y no aplica muchas optimizaciones. Nunca midas el rendimiento con
next devni lo uses para servir una aplicación real.
4.2. next build: compilar para producción
✓ Running next.config.ts took 546ms
- Cache Components enabled
- Partial Prefetching enabled
Creating an optimized production build ...
✓ Compiled successfully in 20.1s
Running TypeScript ...
Finished TypeScript in 23.4s ...
Collecting page data using 5 workers ...
✓ Generating static pages using 5 workers (4/4) in 23.6s
Finalizing page optimization ...
Route (app)
┌ ◐ /
└ ○ /_not-found
next build hace mucho más que compilar:
- Compila y optimiza todas las rutas: minifica, divide el JavaScript en trozos (chunks) y separa el código de servidor del de cliente.
- Comprueba los tipos con TypeScript. Un error de tipos detiene el build, aunque
next devte hubiera dejado seguir. - Prerenderiza: ejecuta tus componentes para generar por adelantado todo lo que se pueda (páginas estáticas completas y armazones de las parciales). Para ello ejecuta tu código, así que si una página estática pide datos a la API, la API tiene que estar en marcha durante el build.
- Imprime la tabla de rutas con el tipo de cada una (
○,◐,ƒ).
📘 Concepto: En Next.js 15,
next buildmostraba además el tamaño en kB de cada ruta (“First Load JS”). Next.js 16 eliminó esas cifras porque, según su guía de actualización, eran inexactas en aplicaciones con Server Components: ni siquiera Turbopack y Webpack coincidían al contar la carga útil de los Client Components. Para analizar tamaños se usa el analizador de paquetes (módulo 20).
El resultado queda en .next. Dentro de .next/server/app encontrarás, para la página del apartado 2.1, un fichero index.html de 1 764 bytes: es el armazón prerenderizado. Contiene “Cargando departamentos…” y ningún departamento, porque esa parte es dinámica. A su lado, index.meta guarda las cabeceras y un campo postponed con el estado necesario para reanudar el render en cada petición justo donde se quedó.
4.3. next start: servir en producción
▲ Next.js 16.4.3
- Local: http://127.0.0.1:5500
- Network: http://127.0.0.1:5500
✓ Ready in 6.4s
next start no compila nada: lee .next y sirve. Si no has ejecutado antes next build, falla. Ojo, porque llega a imprimir “Ready” antes de darse cuenta:
✓ Ready in 4.3s
Error: Could not find a production build in the '.next' directory. Try building your app with 'next build' before starting the production server.
Si cambias el código, tienes que volver a compilar. En el módulo 23 verás cómo se empaqueta esto en Docker o se despliega en un proveedor.
next dev | next build | next start | |
|---|---|---|---|
| Cuándo | Mientras programas | Antes de publicar | En el servidor de producción |
| Compila | Bajo demanda, ruta a ruta | Todo, optimizado | Nada |
| Comprueba tipos | No (solo el editor) | Sí, y falla si hay errores | No |
| Prerenderiza | No, todo se genera en cada petición | Sí | Usa lo prerenderizado |
| Recarga al guardar | Sí | No aplica | No |
| Carpeta | .next/dev | .next | Lee .next |
| Equivalente en Vite | vite | vite build | vite preview (pero next start sí es para producción) |
5. Qué pasa en una petición, paso a paso
Con la página del apartado 2.1 compilada y servida con next start, vamos a seguir una petición de principio a fin con herramientas reales: curl, el contenido de .next y el navegador.
Figura 6. Los ocho pasos de una petición. En naranja, lo que viaja en la respuesta HTML.
5.1. Las cabeceras de la respuesta
Estas son las cabeceras reales de GET /:
HTTP/1.1 200 OK
Vary: rsc, next-router-state-tree, next-router-prefetch, next-router-segment-prefetch, Accept-Encoding
x-nextjs-stale-time: 300
x-nextjs-prerender: 1
x-nextjs-postponed: 1
X-Powered-By: Next.js
Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
x-nextjs-prerender: 1: la respuesta parte de algo prerenderizado en el build (el armazón).x-nextjs-postponed: 1: hay una parte aplazada (la lista) que se ha renderizado en esta petición.Transfer-Encoding: chunked: la respuesta se envía por trozos, sin saber de antemano su longitud. Es la base técnica del streaming.Cache-Control: private, no-cache, no-store: como la página tiene una parte dinámica, ni el navegador ni una CDN deben guardar la respuesta completa.Vary: rsc, next-router-...: la misma URL puede devolver HTML o la carga útil RSC según las cabeceras de la petición. Lo verás en el paso 8.
Medido con curl, el primer byte de la respuesta llegó en unos 0,1 s, y la respuesta completa, entre 0,6 y 1,3 s según la ejecución. La API, por su parte, respondía en menos de 0,04 s. La diferencia es el trabajo del servidor al renderizar la parte dinámica (en un equipo cargado). Lo importante es que el usuario empieza a ver la página con el primer byte, sin esperar a la respuesta completa.
5.2. El HTML y la carga útil RSC
Al final del HTML hay varios <script> con este aspecto (recortados):
self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:\"/_next/static/chunks/1tq6ppe__m8m-.js\"\n3:I[39756,...
self.__next_f.push([1,"8:[\"$\",\"ul\",null,{\"className\":\"space-y-1\",\"children\":[[\"$\",\"li\",\"10\",{...\"children\":\"DIRECCIÓN\"}...
Eso es la carga útil RSC (RSC Payload): una representación compacta del árbol de Server Components ya renderizado. Según la documentación de Next.js, contiene tres cosas:
- El resultado de los Server Components (el
<ul>con sus<li>, como datos, no como código). - Huecos donde van los Client Components, con una referencia a su fichero JavaScript.
- Las props que un Server Component pasa a un Client Component.
La referencia al contador aparece así en la página real:
a:I[98700,["$2","$9"],"Contador"]
Es una instrucción para React: “en este hueco va el componente exportado como Contador del módulo 98700, que está en estos ficheros”. El navegador no recibe el código de Inicio ni el de ListaDepartamentos, solo su resultado y la dirección del código de Contador.
📘 Concepto: El HTML y la carga útil RSC llevan la misma información en dos formatos. El HTML es para que el navegador pinte algo al instante. La carga útil es para que React reconstruya el árbol de componentes, hidrate los Client Components y, más adelante, actualice la página al navegar sin recargarla. Por eso los nombres de los departamentos aparecen dos veces en el documento.
5.3. Comprobar qué código viaja
Puedes demostrar que el código de servidor no viaja buscando dentro de los ficheros que se envían al navegador, que están en .next/static:
grep -rl "Me has pulsado" .next/static
grep -rl "departamentos" .next/static
grep -rl "API_URL" .next/static
Resultado real:
.next/static/chunks/1sti3nkibdny_.js
Solo la primera búsqueda encuentra algo: el texto del botón está en un chunk de cliente, porque Contador es un Client Component. Ni la palabra departamentos (que solo aparece en la URL del fetch y en el texto del servidor) ni el nombre de la variable de entorno aparecen en ningún fichero enviado al navegador.
En total, .next/static contiene 10 ficheros JavaScript (unos 576 kB sin comprimir, en disco). No todos se descargan en cada página: el navegador pide solo los que necesita la ruta, y next start los sirve comprimidos con gzip y con Cache-Control: public, max-age=31536000, immutable, es decir, el navegador puede guardarlos un año. Eso es posible porque cada nombre de fichero incluye un hash de su contenido: si el código cambia, cambia el nombre.
5.4. La hidratación y las navegaciones siguientes
Cuando llegan los ficheros JavaScript, React:
- Lee la carga útil RSC de
self.__next_f. - Recorre el árbol y, al llegar al hueco de
Contador, ejecuta su código sin volver a crear el DOM: reutiliza el<button>que ya estaba y le engancha elonClick. Esto es la hidratación, que ya conoces del Curso de React.
A partir de ahí, si la aplicación tuviera más páginas enlazadas con <Link>, las navegaciones ya no pedirían HTML: Next.js pide solo la carga útil RSC de la nueva ruta (de ahí la cabecera Vary: rsc), la guarda en una caché del navegador y React actualiza únicamente la parte de la página que cambia. El estado de los Client Components que siguen en pantalla se conserva. Lo verás en el módulo 03.
⚠️ Importante: Si intentas pedir la carga útil “a mano” con
curl -H "RSC: 1" http://.../, el servidor responde con una redirección307a/?_rsc. Next.js exige un parámetro_rsccalculado a partir de las cabeceras del router para que las respuestas RSC no se mezclen en las cachés intermedias con las respuestas HTML. Es un detalle interno: tú nunca construirás estas peticiones, las hace<Link>.
6. Medirlo: Next.js frente a una SPA con Vite
Hasta aquí, todo son argumentos. Toca medir. Para ello se ha construido la misma página como SPA con React 19.3 y Vite, con los mismos datos y el mismo botón:
// src/App.tsx (SPA con Vite)
import { useEffect, useState } from 'react'
const API = import.meta.env.VITE_API_URL ?? 'http://localhost:3000/api'
type Departamento = {
deptId: number
nombre: string
localidad: string
}
function ListaDepartamentos() {
const [departamentos, setDepartamentos] = useState<Departamento[] | null>(null)
// En una SPA los datos se piden desde el navegador, después del primer render
useEffect(() => {
fetch(`${API}/departamentos`)
.then((r) => r.json())
.then((datos: { contenido: Departamento[] }) => setDepartamentos(datos.contenido))
}, [])
if (!departamentos) return <p>Cargando departamentos...</p>
return (
<ul className="lista">
{departamentos.map((d) => (
<li key={d.deptId}>
<strong>{d.nombre}</strong> ({d.localidad})
</li>
))}
</ul>
)
}
function Contador() {
const [visitas, setVisitas] = useState(0)
return (
<button onClick={() => setVisitas(visitas + 1)}>
Me has pulsado {visitas} veces
</button>
)
}
export function App() {
return (
<main>
<h1>EMPRESA</h1>
<p>Departamentos leídos desde el navegador:</p>
<ListaDepartamentos />
<Contador />
</main>
)
}
vite build la compiló en unos 5 segundos:
dist/index.html 0.40 kB │ gzip: 0.27 kB
dist/assets/index-DkwuBXSx.css 0.35 kB │ gzip: 0.23 kB
dist/assets/index-DlvYlq0H.js 220.20 kB │ gzip: 68.90 kB
Además, se añadió a Next.js una tercera variante, /cacheada, en la que la lista se cachea en lugar de generarse en cada petición. Usa 'use cache', que estudiarás en el módulo 09; por ahora basta con saber que le dice a Next.js “este resultado puede guardarse durante horas”:
// app/cacheada/page.tsx
import { cacheLife } from 'next/cache'
const API = process.env.API_URL ?? 'http://localhost:3000/api'
type Departamento = {
deptId: number
nombre: string
localidad: string
}
// Los departamentos cambian poco: se guardan en caché durante horas
async function ListaDepartamentos() {
'use cache'
cacheLife('hours')
const respuesta = await fetch(`${API}/departamentos`)
const datos: { contenido: Departamento[] } = await respuesta.json()
return (
<ul className="space-y-1">
{datos.contenido.map((d) => (
<li key={d.deptId} className="rounded bg-indigo-50 px-3 py-1">
<strong>{d.nombre}</strong> ({d.localidad})
</li>
))}
</ul>
)
}
export default function Cacheada() {
return (
<main className="mx-auto max-w-xl space-y-6 p-8">
<h1 className="text-3xl font-bold text-indigo-900">EMPRESA</h1>
<ListaDepartamentos />
</main>
)
}
next build la clasifica como completamente estática, con su duración de caché:
Route (app) Revalidate Expire
┌ ◐ /
├ ○ /_not-found
└ ○ /cacheada 1h 1d
Y al servirla, las cabeceras cambian: x-nextjs-cache: HIT y Cache-Control: s-maxage=3600, stale-while-revalidate=82800. Ahora sí, una CDN puede guardar la página una hora.
6.1. Cómo se ha medido
Las tres páginas se sirvieron en producción (next start y vite preview) y se midieron con Chromium controlado por Playwright, cinco veces cada una, con la caché del navegador desactivada. El script registra los bytes recibidos por tipo de recurso, el número de peticiones, el FCP (First Contentful Paint, el momento en que el navegador pinta el primer contenido) y el momento en que el texto INVESTIGACIÓN, el último departamento, aparece en pantalla. Se tomó la mediana de las cinco ejecuciones.
Se hicieron dos rondas: sin limitar nada y simulando un móvil modesto (latencia de 150 ms, unos 200 kB/s de bajada y la CPU cuatro veces más lenta, con las herramientas de Chrome).
6.2. Resultados
| Medida | SPA con Vite | Next.js / (streaming) | Next.js /cacheada |
|---|---|---|---|
| Documento HTML (comprimido) | 659 B | 5 887 B | 2 941 B |
| JavaScript descargado (comprimido) | 68 577 B | 138 236 B | 137 519 B |
| Datos pedidos desde el navegador | 989 B | 0 B | 0 B |
| Peticiones | 4 | 8 | 7 |
| FCP, sin limitar | 248 ms | 236 ms | 320 ms |
| Lista visible, sin limitar | 504 ms | 1 080 ms | 356 ms |
| FCP, móvil simulado | 1 960 ms | 1 040 ms | 872 ms |
| Lista visible, móvil simulado | 2 659 ms | 2 862 ms | 1 109 ms |
| Con JavaScript desactivado | Página en blanco | Armazón y “Cargando…” | Página completa |
(/cacheada no tiene el botón, por eso su HTML es más pequeño.)

Figura 7. La página / con JavaScript desactivado. El armazón llega, pero la lista no se coloca en su sitio, porque lo hace un script en línea.

Figura 8. La página /cacheada con JavaScript desactivado. La lista forma parte del HTML prerenderizado, así que se ve aunque no se ejecute nada.
6.3. Cómo leer estos números
Los números no dicen “Next.js es más rápido”, y es importante entender por qué:
- Next.js envía más JavaScript que la SPA: unos 138 kB frente a 69 kB. Esa es la base fija del framework: el router del App Router, el código que interpreta la carga útil RSC y la gestión de la caché del cliente. En una página de juguete pesa más que toda la aplicación. En una aplicación real, en cambio, los Server Components no añaden JavaScript por mucho código que tengan, mientras que en una SPA cada componente nuevo suma.
- El primer pintado gana claramente en el móvil: 1,0 s (o 0,9 s cacheada) frente a 2,0 s. El navegador puede pintar el HTML del armazón antes de descargar y ejecutar el JavaScript. En la SPA, con la CPU lenta, no hay nada que pintar hasta que React se ejecuta.
- La lista dinámica con streaming no llega antes que en la SPA. En el móvil simulado, 2,9 s frente a 2,7 s; sin limitar, incluso tarda más (1,1 s frente a 0,5 s). El servidor tiene que renderizar esa parte en cada petición, y en el equipo de pruebas ese trabajo costaba entre medio segundo y un segundo. La ventaja del streaming no es que el dato llegue antes, sino que el resto de la página no espera por él.
- La versión cacheada gana en todo lo que importa al usuario: 1,1 s hasta ver la lista en el móvil, menos de la mitad que la SPA, y una página completa incluso sin JavaScript. Es la razón por la que el catálogo de la aplicación EMPRESA será estático y cacheado.
- Sin JavaScript, la SPA no muestra nada. Esto importa para los buscadores que no ejecutan JavaScript, para las vistas previas de enlaces y para cualquier fallo de red que impida descargar los scripts.
💡 Consejo: La conclusión práctica es la que guía todo el curso: lo que pueda ser estático o cacheado, que lo sea (catálogo, fichas de producto, páginas informativas); lo que tenga que ser dinámico, que vaya dentro de
<Suspense>para no bloquear lo demás; y que los Client Components sean pocos y pequeños. Next.js no hace rápida una aplicación por sí solo: te da las herramientas para que lo sea.
⚠️ Importante: Estos números se tomaron en un único equipo, con otros procesos en marcha, y la página es diminuta. Sirven para entender tendencias, no como cifras absolutas. Si quieres conclusiones para tu proyecto, mide tu proyecto (ejercicio 12).
7. Un poco de historia
Saber de dónde viene Next.js explica por qué hay tantas formas de hacer lo mismo y por qué un tutorial de hace tres años puede no servirte.
Figura 9. Los hitos principales. En naranja, los dos cambios de modelo que más afectan a cómo se escribe una aplicación.
7.1. Los orígenes: SSR para React (2016-2019)
La primera versión se publicó como código abierto en GitHub el 25 de octubre de 2016. Su autor principal fue Guillermo Rauch, fundador de la empresa ZEIT. La idea inicial era muy concreta: hacer fácil algo que en 2016 era muy difícil, renderizar React en el servidor. Cada fichero dentro de una carpeta pages/ se convertía en una ruta, y Next se encargaba de compilar, servir el HTML y enviar el JavaScript.
Ese modelo se conoce hoy como Pages Router. Durante años añadió piezas: rutas de API en la versión 9 (2019), y en 2020 la generación estática y la regeneración estática incremental (ISR, en la versión 9.5), que permitía regenerar páginas estáticas sin recompilar todo el sitio.
7.2. De ZEIT a Vercel (2020)
El 21 de abril de 2020 ZEIT cambió su nombre a Vercel, con una nueva ronda de financiación. Vercel ofrece una plataforma de alojamiento y es quien mantiene Next.js: su director ejecutivo es Guillermo Rauch y el responsable del proyecto, Tim Neutkens.
7.3. El compilador en Rust y el App Router (2021-2023)
- Next.js 12 (octubre de 2021) introdujo un compilador escrito en Rust y el middleware.
- Next.js 13 (octubre de 2022) presentó, en beta, el App Router: una carpeta
app/con layouts anidados y React Server Components. Fue el mayor cambio de la historia del proyecto. El App Router pasó a ser estable en la versión 13.4 (mayo de 2023). - Next.js 14 (octubre de 2023) declaró estables las Server Actions y presentó una vista previa del prerenderizado parcial (PPR).
Durante esta etapa, el App Router tenía un sistema de caché implícito: muchas cosas se guardaban en caché sin que lo pidieras, y eso fue la mayor fuente de quejas y confusiones de la comunidad.
7.4. Next.js 15 y 16 (2024-2026)
- Next.js 15 (21 de octubre de 2024) adoptó React 19, hizo asíncronas las APIs de petición (
cookies(),headers(),params…) y dejó de cachearfetchpor defecto. - Next.js 16 (21 de octubre de 2025) hizo Turbopack el bundler por defecto, presentó Cache Components (caché explícita con
'use cache'), renombrómiddlewareaproxyy eliminó todo lo que llevaba tiempo marcado como obsoleto. - Las versiones menores 16.1 a 16.4 han ido completando el modelo: caché en disco para el desarrollo, la Adapter API estable para desplegar en cualquier proveedor, navegaciones instantáneas y, en 16.4 (octubre de 2026), la recomendación oficial de Cache Components como el mejor modelo para cualquier aplicación. Será el modelo por defecto en Next.js 17.
📘 Concepto: Hoy conviven tres generaciones de código Next.js en Internet: Pages Router (
pages/,getServerSideProps), App Router con la caché anterior (fetchconnext.revalidate,export const revalidate,unstable_cache) y App Router con Cache Components ('use cache',cacheLife,cacheTag). Este curso enseña la tercera, explica la segunda para quien siga en Next.js 15 y te enseña a leer la primera en el módulo 24.
7.5. Next.js y Vercel: ¿estoy atado a una empresa?
Es una pregunta razonable, y la respuesta corta es no. Next.js tiene licencia MIT, y una aplicación Next.js se puede ejecutar en cualquier sitio donde haya Node.js:
- Con
next buildynext starten tu propio servidor, como en los apartados 4 a 6. - En un contenedor Docker con
output: 'standalone'. - Como sitio estático puro con
output: 'export', si no necesitas servidor. - En otros proveedores mediante la Adapter API, estable desde la 16.2.
Lo que sí es cierto es que algunas funciones (la caché distribuida, la optimización de imágenes a gran escala, el ISR en varias regiones) son más fáciles de aprovechar en Vercel, porque su plataforma está hecha a medida. En otros proveedores puede que tengas que configurar más cosas. Lo verás con detalle en el módulo 23.
8. Next.js frente a las alternativas
Next.js no es la única opción, ni siempre la mejor. Antes de elegir, conviene saber qué ofrece cada alternativa y qué precio tiene.
8.1. React + Vite (SPA)
Es lo que hiciste en el Curso de React y lo que has medido en el apartado 6. Vite compila y sirve tu código; React construye la interfaz en el navegador; tú añades un router y una biblioteca de datos.
- Ventajas: modelo mental simple (todo pasa en el navegador), alojamiento estático y barato, ningún servidor que mantener, arranque y recarga en caliente muy rápidos, compilación rápida (5 s frente a más de 40 s de
next builden el ejemplo, incluida la comprobación de tipos), máxima libertad para elegir piezas. - Inconvenientes: los cinco problemas del apartado 2. No hay Server Components ni Server Actions.
- Cuándo elegirlo: aplicaciones internas detrás de un login (un panel de administración, una herramienta de gestión), donde los buscadores no importan y los usuarios tienen buenos equipos. También cuando el backend ya existe en otro lenguaje (Java, .NET, Python) y solo necesitas la interfaz.
8.2. React Router v7 (antes Remix)
Remix nació como un framework centrado en los estándares web: formularios HTML, Request y Response, mejora progresiva. En mayo de 2024 sus autores anunciaron que lo que iba a ser Remix v3 se publicaría como React Router v7, de modo que hoy React Router puede funcionar como simple router de una SPA o como framework full stack con Vite. La marca Remix sigue existiendo para proyectos nuevos de sus autores.
- Ventajas: usa Vite, se basa mucho en estándares web, tiene un modelo de carga de datos (loaders y actions) fácil de razonar, y te permite empezar como SPA e ir añadiendo servidor poco a poco.
- Inconvenientes: su soporte de Server Components es más reciente que el de Next.js y el ecosistema de ejemplos es menor.
- Cuándo elegirlo: si ya tienes una SPA con React Router y quieres añadir renderizado en servidor sin cambiar de herramienta, o si prefieres un modelo más cercano a HTTP.
8.3. Astro
Astro es un framework orientado a sitios de contenido: blogs, documentación, webs corporativas, tiendas con mucho catálogo y poca interacción. Por defecto genera HTML sin nada de JavaScript, y solo hidrata las “islas” interactivas que marques. Además, esas islas pueden escribirse con React, Vue, Svelte u otros.
- Ventajas: páginas muy ligeras, ideal para contenido, permite mezclar frameworks. Evita la base fija de JavaScript que has visto en el apartado 6.3.
- Inconvenientes: no está pensado para aplicaciones muy interactivas con mucho estado compartido entre partes de la página.
- Cuándo elegirlo: cuando lo importante es el contenido y la interacción es puntual.
8.4. Nuxt
Nuxt es a Vue lo que Next.js es a React: un framework full stack con rutas por carpetas, renderizado en servidor, generación estática y rutas de API. Si vienes del mundo Vue, Nuxt es la referencia.
- Cuándo elegirlo: cuando el equipo trabaja con Vue. Elegir entre Next.js y Nuxt es, sobre todo, elegir entre React y Vue.
8.5. Y también: TanStack Start, Angular
La documentación de React menciona también TanStack Start, un framework full stack construido sobre TanStack Router, todavía joven. Y en el mundo Angular, el propio framework incluye renderizado en servidor e hidratación (lo verás en el Curso de Angular). Son opciones válidas, pero con menos material y menos ofertas de empleo que Next.js.
8.6. Tabla comparativa
| React + Vite | Next.js | React Router v7 | Astro | Nuxt | |
|---|---|---|---|---|---|
| Biblioteca de interfaz | React | React | React | Cualquiera (islas) | Vue |
| Dónde se genera el HTML | Navegador | Servidor, build o ambos | Servidor o navegador | Sobre todo build | Servidor, build o ambos |
| Server Components | No | Sí, modelo por defecto | Más recientes | No (usa islas) | No (Vue tiene su propio modelo) |
| Backend en el mismo proyecto | No | Sí | Sí | Sí, limitado | Sí |
| Necesita servidor | No | Normalmente sí | Según el modo | Normalmente no | Normalmente sí |
| Curva de aprendizaje | Baja | Alta | Media | Baja | Media |
| Ideal para | Paneles internos, SPAs | Aplicaciones completas con SEO y datos | Apps centradas en formularios y HTTP | Sitios de contenido | Apps completas con Vue |
💡 Consejo: No elijas framework por moda. Hazte tres preguntas: ¿importan los buscadores o la primera carga en móviles? ¿Necesito código de servidor propio? ¿Qué sabe ya mi equipo? Si las dos primeras son “no”, una SPA con Vite probablemente sea lo más sencillo. Si son “sí” y el equipo sabe React, Next.js es la opción más madura.
9. Versiones, LTS y soporte
Next.js sigue el versionado semántico: 16.4.3 significa versión mayor 16, menor 4, parche 3.
- Las mayores (15, 16, 17) salen aproximadamente una vez al año, en octubre, y pueden romper código.
- Las menores (16.1, 16.2…) salen cada pocos meses y añaden funciones sin romper lo que ya funciona.
- Los parches (16.4.3, 16.4.4…) corrigen fallos y vulnerabilidades. Salen a menudo, y conviene instalarlos siempre.
Además existe un canal canary, con versiones de prueba que se publican casi a diario. Sirven para probar funciones futuras, pero el propio equipo de Next.js desaconseja usarlas en producción.
9.1. La política de soporte
La política oficial de soporte (nextjs.org/support-policy) define dos fases para cada versión mayor:
- Active LTS: la versión mayor más reciente. Recibe funciones nuevas, correcciones, mejoras de rendimiento y parches de seguridad. Dura hasta que sale la siguiente mayor.
- Maintenance LTS: cuando sale una mayor nueva, la anterior pasa a esta fase. Solo recibe correcciones críticas y parches de seguridad, y dura dos años desde su lanzamiento original.
| Versión mayor | Lanzamiento | Estado en noviembre de 2026 |
|---|---|---|
| 16.x | 21-10-2025 | Active LTS |
| 15.x | 21-10-2024 | Maintenance LTS hasta el 31-01-2027 (ampliado) |
| 14.x y anteriores | 26-10-2023 y antes | Sin soporte |
La fase de mantenimiento de Next.js 15 debía terminar el 21-10-2026, pero el equipo la amplió hasta el 31-01-2027 para dar más tiempo a quien migra a la 16. La última versión de la rama 15 es la 15.5.28, que npm publica con la etiqueta backport.
Figura 10. Cuando sale una versión mayor, la anterior pasa a mantenimiento. La línea discontinua marca la fecha de este curso.
📘 Concepto: En muchos proyectos (Node.js, Angular, Java) “LTS” es una versión especial, distinta de la última. En Next.js la última mayor también es LTS: Next.js 16 es a la vez la versión más reciente y la que tiene soporte activo. Lo que cambia es que Next.js 15, aún con soporte, ya no recibe funciones nuevas.
9.2. Los parches de seguridad
En diciembre de 2025 se descubrieron vulnerabilidades graves en la forma en que React y Next.js procesan las peticiones de Server Components (la primera fue la CVE-2025-66478, conocida como React2Shell). Hubo que publicar parches urgentes en varias ramas. Las estudiarás en el módulo 22.
A raíz de aquello, en julio de 2026 Next.js formalizó un programa de publicaciones de seguridad: aproximadamente una vez al mes se anuncia en el blog oficial, con antelación, qué día saldrán los parches y la gravedad máxima de las vulnerabilidades que corrigen. Los fallos urgentes o ya explotados se siguen corrigiendo de inmediato, sin esperar al calendario. Los parches salen tanto para la rama activa (16.x) como para la de mantenimiento (15.5.x).
9.3. La versión de este curso: Next.js 16.4
Este curso se llama “de Next.js 16.4”, no “de Next.js 16.4.3”, y la diferencia importa. Los ejemplos se verificaron con 16.4.3, que era la última versión el 23 de octubre de 2026. El 2 de noviembre de 2026 se publicó 16.4.4, y next info lo avisa así en un proyecto con 16.4.3:
Relevant Packages:
next: 16.4.3 // There is a newer version (16.4.4) available, upgrade recommended!
Cuando leas esto habrá probablemente más parches. Tu regla debe ser: la menor del curso (16.4) con el último parche disponible. Hay tres motivos:
- Seguridad. Con el calendario mensual, cada pocas semanas sale un parche que corrige vulnerabilidades. Una aplicación Next.js tiene un servidor expuesto a Internet: un fallo en cómo procesa las peticiones de Server Components o de Server Actions afecta a todas las aplicaciones que no se actualicen, como pasó en diciembre de 2025.
- Riesgo mínimo. Un parche no cambia APIs ni comportamientos por defecto. Por eso puedes seguir este curso con 16.4.4 o posteriores sin que cambie nada de lo que lees.
- Correcciones de fallos. Si algo no funciona como debería, lo primero que te pedirán en una incidencia de GitHub es probar con la última versión.
# Instala la última 16.4.x (y las versiones de React que pide)
npm install next@16.4 react@latest react-dom@latest
# Comprueba la versión instalada
npx next --version
⚠️ Importante: En una aplicación con servidor, no actualizar es un riesgo real. Suscríbete al blog de Next.js (
nextjs.org/blog) y aplica los parches de tu rama en cuanto salgan. Si usas un gestor de dependencias automático (Dependabot, Renovate), configúralo para que proponga los parches denexten cuanto se publiquen.
10. ¿Qué versión elegir? Next.js 16 o Next.js 15
Este curso usa Next.js 16.4. Pero muchos proyectos reales siguen en Next.js 15, y el salto de la 15 a la 16 es grande. Por eso el curso indica, apartado por apartado, qué no tendrás si sigues en Next.js 15 y qué hacer en su lugar.
10.1. Qué cambia de la 15 a la 16
Estos son los cambios más visibles. Cada uno se estudia en su módulo.
| Tema | Next.js 15 | Next.js 16 |
|---|---|---|
| Caché | Modelo anterior: fetch con cache y next.revalidate, export const revalidate, unstable_cache | Cache Components: 'use cache', cacheLife, cacheTag, updateTag (el modelo anterior sigue disponible si no lo activas) |
| Bundler | Webpack por defecto; Turbopack con --turbopack | Turbopack por defecto; Webpack con --webpack |
| Interceptar peticiones | middleware.ts | proxy.ts (runtime de Node.js) |
| APIs de petición | Asíncronas, con acceso síncrono aún tolerado con aviso | Solo asíncronas |
next lint | Existe | Eliminado: se usa eslint directamente |
revalidateTag | Un argumento | Exige un segundo argumento (perfil) |
| Navegación | Prefetch clásico | Prefetch incremental, navegaciones instantáneas (16.3) |
Salida de next build | Con tamaños por ruta | Sin tamaños; con tiempos por fase |
Carpeta de next dev | .next | .next/dev |
| Requisitos | Node.js 18.18+ | Node.js 20.9+, TypeScript 5.1+, navegadores modernos (Chrome/Edge/Firefox 111+, Safari 16.4+) |
| Fin de soporte | 31-01-2027 | Activo hasta la 17; después, dos años desde octubre de 2025 |
10.2. Un mismo código, dos comportamientos 🆕 16.0
🆕 Desde Next.js 16.0. La opción
cacheComponentsno existe en Next.js 15. Si usas Next.js 15: una página que lee datos sin marcar se prerenderiza en elbuildcon los datos de ese momento; para que se genere en cada petición añadeexport const dynamic = 'force-dynamic'a la página (en Next.js 16 concacheComponentsesa línea rompe elbuild).
La diferencia de filosofía se ve con un ejemplo muy pequeño. Esta página lee el número de departamentos sin <Suspense> y sin caché:
// app/sin-suspense/page.tsx
const API = process.env.API_URL ?? 'http://localhost:3000/api'
export default async function SinSuspense() {
const respuesta = await fetch(`${API}/departamentos`)
const datos: { total: number } = await respuesta.json()
return <p>Hay {datos.total} departamentos</p>
}
En un proyecto recién creado con Next.js 16.4, que activa Cache Components, next build falla con este mensaje real:
Error: Route "/sin-suspense": Next.js encountered uncached or runtime data during prerendering.
`fetch(...)`, `cookies()`, `headers()`, `params`, `searchParams`, or `connection()` accessed outside of `<Suspense>` prevents the route from being prerendered, blocking the page load and leading to a slower user experience.
Ways to fix this:
- [stream] Provide a placeholder with `<Suspense fallback={...}>` around the data access
- [cache] For uncached data (`fetch`, database calls): cache the access with `"use cache"` (does not apply to `connection()`)
- [block] Set `export const instant = false` to allow a blocking route
En Next.js 15.5.28, con la plantilla de create-next-app, la misma página compila sin errores:
Route (app) Size First Load JS
┌ ○ / 5.47 kB 119 kB
├ ○ /_not-found 0 B 114 kB
└ ○ /sin-suspense 0 B 114 kB
○ (Static) prerendered as static content
¿Cuál de los dos tiene razón? Mira el símbolo: ○ (Static). Next.js 15 hizo el fetch una sola vez, durante el build, guardó el HTML resultante y lo servirá así para siempre. Si mañana se crea un departamento nuevo, la página seguirá diciendo “Hay 7 departamentos” hasta la próxima compilación. No hay error, pero tampoco aviso: es un dato congelado sin que lo hayas pedido.
Next.js 16 con Cache Components se niega a adivinar. Te obliga a decidir entre las dos primeras soluciones que propone el mensaje, que son justo las que has visto en el apartado 6: o el dato se cachea a propósito ('use cache', como en /cacheada), o se genera en cada petición y le das un sitio de espera (<Suspense>, como en /) para que el resto de la página pueda servirse al instante. Es más estricto, pero los errores aparecen al compilar, no en producción.
💡 Consejo: Este ejemplo resume por qué la 16 se recomienda para proyectos nuevos: las decisiones de caché pasan de ser implícitas a ser explícitas. Lo estudiarás en profundidad en el módulo 09.
10.3. Criterio para decidir
| Situación | Recomendación |
|---|---|
| Proyecto nuevo | Next.js 16, con el último parche de la última menor. Tienes soporte activo, Cache Components y Turbopack. |
| Estás aprendiendo | Next.js 16. Es lo que encontrarás en la documentación oficial actual. |
| Proyecto en producción con Next.js 15 y poco margen | Sigue en 15.5.x, aplica todos los parches de seguridad y planifica la migración antes del 31-01-2027. |
| Proyecto en producción con Next.js 14 o anterior | No tiene soporte: migra cuanto antes. Pasa primero a 15 y después a 16, o directamente a 16 con los codemods (módulo 24). |
| Dependes de una biblioteca que aún no funciona con la 16 | 15.5.x mientras tanto, vigilando la fecha de fin de soporte. |
| Servidor con Node.js 18 que no puedes actualizar | Next.js 16 no funciona. Actualiza Node.js; si no puedes, 15.5.x temporalmente. |
⚠️ Importante: Quedarse en Next.js 15 tiene fecha de caducidad. A partir del 31-01-2027 no habrá más parches de seguridad para esa rama, y con el ritmo actual de descubrimiento de vulnerabilidades, una aplicación con servidor sin parches es un riesgo serio.
11. Cómo leer las marcas de este curso
Como el curso enseña Next.js 16.4 pero quiere servirte también si trabajas con Next.js 15, cada apartado afectado por una diferencia de versión lleva una marca al final de su título:
- 🆕 16.x: la función no existe en Next.js 15.
- 🔄 16.x: la función existe en Next.js 15, pero cambia (de nombre, de comportamiento por defecto o de sintaxis).
Justo debajo del título aparece siempre una caja con lo que pierdes y qué hacer. Ya has visto una real en el apartado 10.2. Así se verá, por ejemplo, en el módulo 13:
🔄 Cambia en Next.js 16.0. En Next.js 15 el fichero se llama
middleware.tsy exporta una funciónmiddleware, que por defecto se ejecuta en el runtime Edge. Si usas Next.js 15: usamiddleware.tscon el mismo contenido y la función llamadamiddleware.
Y así una función que no existe en la 15:
🆕 Desde Next.js 16.4. No existe en Next.js 15. Si usas Next.js 15: no hay equivalente directo; …
Reglas para leerlas:
- Si un apartado no tiene marca, funciona igual en Next.js 15. Las marcas solo se ponen cuando hay diferencia comprobada.
- Las alternativas para Next.js 15 se han compilado en un proyecto real creado con
create-next-app@15.5.28. - Si todo un módulo depende de una novedad, la caja aparece al principio, después de los objetivos.
- Las versiones menores importan: algo marcado
🆕 16.3no existe en la 16.2. Si tu proyecto está en una 16 antigua, actualiza a la última 16.x: es gratis y no rompe nada. - Los parches no llevan marca nunca: entre 16.4.3 y 16.4.4 no cambian las APIs.
💡 Consejo: Si sigues el curso con Next.js 15, lee primero el apartado normal (para entender la idea) y después la caja (para saber cómo hacerlo en tu versión). Así, cuando migres a la 16, ya sabrás qué cambia.
12. Mapa del curso
Figura 11. Los 26 módulos agrupados en seis bloques. Cada bloque añade una capa a la aplicación EMPRESA.
| Bloque | Módulos | Qué aprenderás |
|---|---|---|
| Fundamentos | 00, 01, 02 | Qué es Next.js, cómo crear un proyecto y cómo se genera cada página |
| Rutas y componentes | 03 a 07 | App Router, rutas dinámicas, Server y Client Components, estilos, imágenes y metadatos |
| Datos | 08 a 12 | Leer datos en el servidor, caché, Server Actions, formularios y APIs propias |
| Aplicación completa | 13 a 19 | Proxy, errores, rutas avanzadas, base de datos, autenticación, estado en cliente e idiomas |
| Producción | 20 a 24 | Rendimiento, pruebas, seguridad, despliegue y migración |
| Proyecto final | 25 | La aplicación EMPRESA completa, descargable |
12.1. El hilo conductor: la aplicación EMPRESA
A lo largo del curso construirás una aplicación full stack con dos partes:
- Un catálogo público de la tienda de la empresa (productos, ficha de cada producto), estático y en caché, rápido y bien posicionado en buscadores. Es el caso de
/cacheadadel apartado 6. - Una zona privada de gestión (empleados, departamentos, pedidos) con inicio de sesión y roles, con datos siempre actuales.
Cada módulo se puede leer por separado, pero los ejemplos usan siempre los mismos datos, así que irás reconociendo los departamentos, empleados y pedidos.
12.2. Los datos
- Módulos 00 a 15: la API EMPRESA, la misma de los cursos de React y Angular. Descárgala de
recursos/empresa-api.zip, descomprímela y arráncala connode server.mjs. Queda enhttp://localhost:3000/api. No tiene dependencias: solo necesitas Node.js 22 o superior. - A partir del módulo 16: una base de datos SQLite propia, gestionada con Drizzle, que se carga con los mismos datos.
⚠️ Importante: La API EMPRESA y Next.js usan por defecto el mismo puerto, el 3000. Si arrancas la API primero,
next devdetectará que el puerto está ocupado y probará otro, peronext startno lo hace, y es mejor no depender de eso: arranca Next.js siempre en el 3001 connpm run dev -- -p 3001. Lo verás en el módulo 01.
12.3. Qué necesitas saber antes
- HTML y CSS: estructura de una página, selectores, flexbox. Repásalo en el Curso de HTML y CSS si lo necesitas.
- JavaScript moderno: módulos,
async/await, desestructuración,fetch. Está todo en el Curso de JavaScript. - React: componentes, props, estado,
useState,useEffect, Suspense. Es muy recomendable haber hecho el Curso de React, al menos hasta el módulo de Server Components. Este curso remite a él para todo lo que es de React y no de Next.js. - TypeScript básico: tipos, interfaces, genéricos sencillos. Los ejemplos están en TypeScript, como en el Curso de React.
12.4. Requisitos técnicos
| Herramienta | Versión mínima | Comprobación |
|---|---|---|
| Node.js | 20.9 (recomendada la 24 LTS) | node --version |
| npm | La que trae Node.js | npm --version |
| Navegador | Chrome, Edge o Firefox 111+, Safari 16.4+ | |
| Editor | VS Code o similar, con soporte de TypeScript |
13. Ejercicios
Ejercicio 1: ¿Biblioteca o framework?
Clasifica cada responsabilidad según la asuma React, Next.js o ninguno de los dos (la tienes que resolver tú con otra herramienta):
- Volver a dibujar un componente cuando cambia su estado.
- Convertir el fichero
app/productos/page.tsxen la URL/productos. - Guardar los pedidos en una base de datos.
- Dividir el JavaScript en trozos para que cada página descargue solo lo suyo.
- Definir qué es un Server Component y cómo se serializa su resultado.
- Generar el HTML de una página durante
next build. - Comprobar que el email de un formulario tiene un formato válido.
- Poner un
<title>distinto en cada página.
Ver solución
| Responsabilidad | Quién | Por qué |
|---|---|---|
| 1. Volver a dibujar al cambiar el estado | React | Es exactamente lo que hace React: estado y render. |
| 2. Fichero → URL | Next.js | React no sabe nada de URL. El enrutado por carpetas es una convención de Next.js. |
| 3. Guardar en base de datos | Ninguno | Next.js te da dónde ejecutar el código (Server Actions), pero la base de datos y el ORM los eliges tú (en el curso, SQLite con Drizzle). |
| 4. Dividir el JavaScript | Next.js | Lo hace su bundler, Turbopack, durante next build. |
| 5. Definir Server Components | React | La especificación es de React 19. Next.js la implementa con su servidor y su bundler. |
6. HTML en next build | Next.js | El prerenderizado es una decisión del framework. React solo aporta la función que convierte componentes en HTML. |
| 7. Validar un email | Ninguno | Puedes hacerlo a mano, pero en el curso usarás Zod. |
8. <title> por página | Next.js | Con la Metadata API (export const metadata), como en el layout del apartado 3.2. React 19 también permite poner <title> dentro de un componente, pero Next.js lo integra con el streaming y la caché. |
La respuesta 8 es intencionadamente ambigua: en el límite entre React y Next.js hay zonas grises, y es normal. La regla útil es: si la pieza tiene que ver con URL, servidor, compilación o despliegue, es del framework.
Ejercicio 2: Detectives del HTML
Abre en el navegador una SPA que conozcas (por ejemplo, la que hiciste en el Curso de React con npm run dev) y una web hecha con Next.js (la propia nextjs.org sirve). En cada una:
- Pulsa
Ctrl+U(ver código fuente). ¿Aparece el texto visible de la página dentro del HTML? - Abre las herramientas de desarrollo, pestaña Network, recarga y fíjate en la primera petición (la del documento). ¿Qué tamaño tiene?
- Desactiva JavaScript (en Chrome: DevTools →
Ctrl+Shift+P→ “Disable JavaScript”) y recarga. ¿Qué ves?
Ver solución
- En la SPA, el HTML solo contiene
<div id="root"></div>y una etiqueta<script>: el texto visible no está. En la web de Next.js aparece el contenido completo: títulos, párrafos, enlaces. - El documento de la SPA ocupa muy poco (en la medición del apartado 6, 659 bytes) porque está vacío; casi todo el peso está en los ficheros
.js. El documento de Next.js ocupa bastante más (5 887 bytes en la página de ejemplo), porque lleva el HTML ya construido y, al final, la carga útil RSC en los<script>conself.__next_f.push(...). - Sin JavaScript, la SPA se queda en blanco. La web de Next.js se ve casi igual, con contenido y enlaces que funcionan como enlaces normales; solo dejan de funcionar los elementos interactivos (menús desplegables, botones que cambian estado).
Un matiz que has visto en las figuras 7 y 8: en una página con streaming, las partes que llegan después dentro de un <div hidden> las coloca en su sitio un script en línea. Con JavaScript desactivado verás el texto de carga en lugar de esa parte. Las páginas que deben verse completas sin JavaScript (o para ciertos buscadores) conviene que tengan los datos importantes en el armazón, cacheados, como /cacheada.
Ejercicio 3: ¿Qué código viaja al navegador?
Fíjate en la página del apartado 2.1. Indica, para cada elemento, si su código llega al navegador:
- La función
ListaDepartamentos. - La URL de la API (
process.env.API_URL). - La función
Contador. - El tipo
Departamento. - El componente
Inicio. - El texto “DIRECCIÓN” que aparece en la lista.
Ver solución
| Elemento | ¿Viaja su código? | Explicación |
|---|---|---|
ListaDepartamentos | No | Es un Server Component. Solo viaja su resultado (el <ul> con los <li>). |
API_URL | No | Se lee en el servidor. Una variable de entorno sin el prefijo NEXT_PUBLIC_ nunca se incluye en el JavaScript del cliente (módulo 13). El grep del apartado 5.3 lo confirma. |
Contador | Sí | Está en un fichero con 'use client'. Su código se envía para poder hidratarlo y ejecutar onClick. Es el chunk que contiene “Me has pulsado”. |
Departamento | No | Los tipos de TypeScript desaparecen al compilar, tanto en servidor como en cliente. |
Inicio | No | También es un Server Component (no hay 'use client' en page.tsx). |
| “DIRECCIÓN” | Sí, como dato | Viaja dos veces: dentro del HTML y dentro de la carga útil RSC. Lo que no viaja es el código que lo obtuvo. |
Esta es una diferencia fundamental con una SPA, donde todos los componentes, la URL de la API y la lógica de carga viajan al navegador. Haz la prueba: en el .js que genera vite build para la SPA del apartado 6 aparecen tanto la URL de la API como la palabra departamentos.
Ejercicio 4: Lee las cabeceras
Un compañero te pasa las cabeceras de dos páginas de la misma aplicación Next.js 16.4:
Página A
x-nextjs-cache: HIT
x-nextjs-prerender: 1
Cache-Control: s-maxage=3600, stale-while-revalidate=82800
Página B
x-nextjs-prerender: 1
x-nextjs-postponed: 1
Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate
Transfer-Encoding: chunked
- ¿Qué símbolo (
○,◐oƒ) tendrá cada una en la salida denext build? - ¿Cuál de las dos puede guardar una CDN, y durante cuánto tiempo?
- ¿En cuál verías la página completa con JavaScript desactivado?
Ver solución
- A es
○(estática): viene entera de lo prerenderizado (x-nextjs-prerender), se sirve desde la caché (HIT) y no hay nada aplazado. B es◐(prerenderizado parcial): tiene un armazón prerenderizado y una parte aplazada (x-nextjs-postponed: 1) que se genera en cada petición y llega por streaming (chunked). Son exactamente las cabeceras reales de/cacheaday de/en el apartado 6. - A.
s-maxage=3600permite a una caché compartida (una CDN) guardarla 3 600 segundos, una hora.stale-while-revalidate=82800le permite seguir sirviendo la copia antigua hasta 23 horas más mientras pide una nueva en segundo plano: en total, un día, que coincide con las columnas “Revalidate 1h” y “Expire 1d” denext build. B no se puede guardar:private, no-storelo prohíbe, porque una parte cambia en cada petición. - En A. En B verías el armazón con el texto de carga, porque la parte aplazada se coloca con un script (figura 7).
Ejercicio 5: ¿Qué orden uso?
Indica qué orden (next dev, next build o next start) necesitas en cada situación, y si hay alguna trampa:
- Quieres ver al instante cómo queda un cambio de color mientras programas.
- Quieres saber si una página nueva será estática o dinámica.
- Quieres comprobar cuánto tarda la página en cargar en un móvil.
- Has subido el código al servidor de producción y quieres arrancarlo.
- Tu compañero dice que en su equipo “funciona”, pero en la integración continua falla con un error de tipos.
- La página principal prerenderiza datos de la API EMPRESA y
next buildfalla con un error de conexión.
Ver solución
next dev: recarga en caliente al guardar.next build: es la única que clasifica las rutas (○,◐,ƒ). En desarrollo todo se genera en cada petición, así que no te lo dice.next buildy despuésnext start, nuncanext dev. El modo desarrollo compila bajo demanda, añade código de depuración y no prerenderiza: sus tiempos no se parecen a los reales (la primera petición del apartado 4.1 tardó 6,4 s solo por compilar).next buildynext start. La trampa:next startno compila. Si solo copias el código fuente y ejecutasnext start, falla porque no hay.next.- Su equipo usa
next dev, que no comprueba los tipos del proyecto entero; la integración continua ejecutanext build, que sí lo hace. Solución: ejecutarnpm run buildantes de subir cambios. next buildejecuta tu código para prerenderizar, así que la API tiene que estar en marcha durante el build. Arráncala antes de compilar (o, si la página no debe ser estática, haz que esos datos se lean en cada petición dentro de<Suspense>).
Ejercicio 6: Elige la herramienta
Para cada proyecto, elige entre React + Vite, Next.js, Astro, Nuxt o React Router v7, y justifica en una frase:
- El blog técnico de un departamento de informática, con 300 artículos en Markdown y un buscador.
- Un panel interno de fichajes para 40 empleados, detrás de un login, con un backend en Spring Boot ya hecho.
- Una tienda online de 5 000 productos que debe salir bien en Google y tener carrito, login y gestión de pedidos.
- La web de una empresa cuyo equipo lleva cinco años trabajando con Vue.
- Una SPA con React Router que ya funciona, a la que quieres añadir renderizado en servidor sin cambiar de router ni de bundler.
Ver solución
- Astro. Es contenido casi puro, con interacción puntual (el buscador). Astro genera HTML sin JavaScript y permite una “isla” interactiva para buscar. Next.js también serviría (las páginas serían
○), pero arrastraría la base fija de JavaScript del apartado 6.3 en cada página. - React + Vite. No importan los buscadores, los usuarios están en la red de la empresa y el backend ya existe. Un servidor Next.js solo añadiría una pieza más que desplegar.
- Next.js. Necesita SEO (catálogo prerenderizado y cacheado), datos actualizados (carrito, pedidos), autenticación y código de servidor. Es justo el caso que resuelve, y es el proyecto de este curso.
- Nuxt. El conocimiento del equipo pesa más que las diferencias técnicas entre Nuxt y Next.js, que resuelven problemas muy parecidos.
- React Router v7. Permite pasar del modo SPA al modo framework con el mismo router y con Vite, de forma gradual.
No hay respuestas absolutas: lo importante es la justificación. Una respuesta como “Next.js para todo” no es incorrecta técnicamente, pero ignora el coste de mantener un servidor cuando no hace falta.
Ejercicio 7: Calcula el soporte
Con la política de soporte del apartado 9.1, responde:
- Si Next.js 17 sale el 20 de octubre de 2026, ¿en qué fecha pasaría la 16 a Maintenance LTS? ¿Y hasta cuándo recibiría parches de seguridad?
- Si Next.js 17 se retrasa a marzo de 2027, ¿cambia la fecha de fin de soporte de la 16?
- Un cliente te dice el 15 de febrero de 2027 que su aplicación está en Next.js 15.5.28. ¿Qué le dices?
- Otro cliente está en Next.js 16.4.3 y el blog de Next.js anuncia para la semana que viene parches de seguridad de gravedad alta. ¿Qué versión debe instalar y qué riesgo corre al hacerlo?
Ver solución
- Pasaría a Maintenance LTS el mismo día que sale la 17. El mantenimiento dura dos años desde el lanzamiento original de la 16 (21-10-2025), así que recibiría parches hasta el 21-10-2027, salvo ampliación como la que tuvo la 15.
- No. La fecha final se cuenta desde el lanzamiento de la 16, no desde el de la 17. Lo que cambia es cuánto dura cada fase: la 16 estaría más tiempo en Active LTS (recibiendo funciones nuevas) y menos en Maintenance LTS.
- Que su versión ya no tiene soporte desde el 31-01-2027: no recibirá parches de seguridad aunque se descubran vulnerabilidades. Debe migrar a la 16 (o a la mayor vigente) cuanto antes. El módulo 24 explica cómo hacerlo con los codemods oficiales.
- Debe instalar el parche que salga para su rama (un 16.4.x posterior, o el último de la menor más reciente si ya hubiera una 16.5), con
npm install next@16.4en cuanto se publique, y volver a compilar y desplegar. El riesgo es mínimo: los parches no cambian APIs. El riesgo real es no hacerlo, porque una vez publicado el parche los detalles de la vulnerabilidad se hacen públicos y cualquiera puede intentar explotarla en servidores sin actualizar.
Ejercicio 8: ¿Qué versión tengo?
En un proyecto Next.js (puedes crear uno con npx create-next-app@latest mi-prueba --yes, aunque lo veremos con calma en el módulo 01), ejecuta:
npx next --version
npx next info
npm view next dist-tags
Interpreta la salida: ¿qué versión tienes?, ¿hay una más nueva?, ¿qué significa la etiqueta backport?
Ver solución
Esta es la salida real en el equipo donde se preparó el curso, el 2 de noviembre de 2026 (recortada):
$ npx next --version
Next.js v16.4.3
$ npx next info
Binaries:
Node: 24.19.0
npm: 11.6.2
Relevant Packages:
next: 16.4.3 // There is a newer version (16.4.4) available, upgrade recommended!
react: 19.3.0
react-dom: 19.3.0
typescript: 5.9.3
$ npm view next dist-tags
{
"backport": "15.5.28",
"latest": "16.4.4",
"canary": "16.5.0-canary.36",
...
}
next --versionmuestra la versión instalada en el proyecto.next infomuestra además Node.js, React y TypeScript, y avisa si hay una versión de parche más nueva. Es lo primero que te pedirán si abres una incidencia en GitHub.dist-tagsmuestra las etiquetas de npm:latestes la que instalanpm install next(la última estable de la 16),backportes la última de la rama 15 (la que instalanpm install next@backport) ycanaryes la de pruebas. Fíjate en quecanaryya apunta a la 16.5: la siguiente versión menor está en camino.
Tus números serán seguramente más altos. Si tu versión mayor y menor coinciden con las del curso (16.4.x), todo lo que leas aquí funcionará igual. Si next info te avisa de un parche, instálalo como en el apartado 9.3.
Ejercicio 9: Interpreta las marcas
Tienes un proyecto en Next.js 15.5.28. Mientras sigues el curso, encuentras estos encabezados. Para cada uno, di si puedes usar el código tal cual, si tienes que adaptarlo o si no hay forma de usarlo:
### Rutas dinámicas con [id](sin marca).### params y searchParams asíncronos 🔄 16.0.### ensureStatic 🆕 16.4.### El fichero proxy.ts 🔄 16.0.- Tu proyecto está en Next.js 16.2 y encuentras
### Partial Prefetching 🆕 16.3.
Ver solución
- Tal cual. Sin marca significa que funciona igual en Next.js 15.
- Adaptar, aunque poco. En la 15 ya son asíncronos, pero se tolera el acceso síncrono con un aviso. La caja te dirá exactamente qué cambia; en la práctica, si escribes
await paramscomo en la 16, funciona en las dos. - No hay forma directa. Es una novedad de la 16.4. La caja te dirá qué hacer en su lugar (normalmente, revisar a mano la salida de
next buildpara comprobar que la ruta sigue siendo estática). - Adaptar. En la 15 el fichero se llama
middleware.tsy la función,middleware. La lógica es la misma. - No la tienes, pero la solución es fácil: actualiza a la última 16.x con
npm install next@latest. Las versiones menores no rompen código, así que no hay motivo para quedarse en una 16 antigua.
Ejercicio 10: Predice el resultado
Sin ejecutarlo, predice qué ocurre al hacer next build en Next.js 16.4 (con la configuración de create-next-app) y en Next.js 15.5 con cada una de estas variantes de la página sin-suspense del apartado 10.2:
- La página tal cual.
- La página con el
fetchdentro de un componente envuelto en<Suspense fallback={<p>Cargando...</p>}>. - La página tal cual, pero añadiendo
export const dynamic = 'force-dynamic'.
Ver solución
| Variante | Next.js 16.4 (Cache Components) | Next.js 15.5 |
|---|---|---|
| 1. Tal cual | El build falla: “uncached or runtime data during prerendering”. | Compila. La ruta queda ○ (Static) con el dato congelado en el momento del build. |
2. Con <Suspense> | Compila. La ruta queda ◐ (Partial Prerender): el armazón con “Cargando…” es estático y el número llega por streaming en cada petición. | Compila, igual que en el caso 1: estática, con el dato del build. Suspense no cambia la decisión estática o dinámica en la 15. |
3. Con dynamic = 'force-dynamic' | El build falla: “Route segment config “dynamic” is not compatible with nextConfig.cacheComponents. Please remove it.” | Compila. La ruta pasa a ƒ (Dynamic): se genera en cada petición. |
Todos estos resultados se han obtenido compilando de verdad las seis combinaciones. La variante 2 en la 16 es el mismo patrón que la página / del apartado 2.1. La 3 en la 15 es la alternativa de la caja 🆕 del apartado 10.2. La lección: el mismo código puede comportarse de forma muy distinta según la versión y la configuración, y por eso conviene mirar siempre la lista de rutas que imprime next build.
Ejercicio 11: Las tres generaciones de código
Clasifica cada fragmento como Pages Router, App Router con la caché anterior (Next.js 13-15) o App Router con Cache Components (Next.js 16):
// A
export async function getServerSideProps() {
const res = await fetch('http://localhost:3000/api/empleados')
return { props: { empleados: await res.json() } }
}
// B
export const revalidate = 3600
export default async function Productos() {
const res = await fetch('http://localhost:3000/api/productos')
// ...
}
// C
import { cacheLife } from 'next/cache'
export default async function Productos() {
'use cache'
cacheLife('hours')
const res = await fetch('http://localhost:3000/api/productos')
// ...
}
// D
const res = await fetch('http://localhost:3000/api/productos', {
next: { revalidate: 3600, tags: ['productos'] },
})
Ver solución
- A: Pages Router.
getServerSidePropssolo existe en la carpetapages/. Si lo ves, el código es del modelo original. Lo leerás en el módulo 24. - B: App Router con la caché anterior.
export const revalidatees una opción de segmento del modelo de Next.js 13-15. ConcacheComponentsactivado no se usa (igual quedynamic, rompe elbuild). - C: App Router con Cache Components.
'use cache'ycacheLifeson el modelo actual, el mismo de la página/cacheadadel apartado 6 (módulo 09). - D: App Router con la caché anterior. Las opciones
next.revalidateynext.tagsdefetchpertenecen al modelo de Next.js 13-15.
B y D hacen, a grandes rasgos, lo mismo que C: guardar la lista de productos durante una hora. Si sigues en Next.js 15, B y D son tus herramientas. Si buscas ayuda en Internet, esta clasificación te dirá enseguida si el ejemplo te sirve.
Ejercicio 12 (propuesto): Repite la medición en tu equipo
Reproduce el experimento del apartado 6 con tus propias herramientas:
- Crea la página del apartado 2.1 en un proyecto Next.js y la del apartado 6 en un proyecto Vite (
npm create vite@latest, plantillareact-ts). Arranca la API EMPRESA. - Compila ambas (
npm run build) y sírvelas en producción:npm start -- -p 3001para Next.js ynpm run previewpara Vite. - En Chrome, abre DevTools → Network, marca “Disable cache” y elige la limitación “Slow 4G”. En la pestaña Performance, limita también la CPU (4x).
- Recarga cada página varias veces y anota: tamaño del documento, JavaScript transferido, momento del primer pintado y momento en que aparece la lista.
- Añade la página
/cacheaday repite.
Compara tus resultados con la tabla del apartado 6.2. ¿Se mantienen las tendencias? ¿Qué cambia si añades a la SPA una biblioteca de 50 kB que solo usa la lista (por ejemplo, para formatear presupuestos con Intl envuelto en una biblioteca)? ¿Y si la añades a ListaDepartamentos en Next.js?
Resumen
| Concepto | API o herramienta | Idea clave |
|---|---|---|
| Next.js | next | Framework full stack de React: rutas, servidor, compilación, caché y despliegue |
| Biblioteca frente a framework | Tú llamas a la biblioteca; el framework te llama a ti siguiendo sus convenciones | |
| Server Component | async function sin 'use client' | Se ejecuta solo en el servidor; su código no viaja al navegador |
| Client Component | 'use client' | Se renderiza en el servidor y se hidrata en el navegador para ser interactivo |
| Proyecto nuevo | create-next-app --yes | App Router, TypeScript, Tailwind 4, ESLint flat, AGENTS.md, Cache Components activado |
| Layout raíz | app/layout.tsx | Único sitio con <html> y <body>; exporta metadata |
| Desarrollo | next dev | Compila bajo demanda, recarga al guardar, escribe en .next/dev; no sirve para medir |
| Compilación | next build | Compila, comprueba tipos, prerenderiza y clasifica las rutas (○, ◐, ƒ) |
| Producción | next start | Sirve .next sin compilar; renderiza solo lo dinámico |
| Armazón y streaming | <Suspense> | Lo estático sale al instante; lo dinámico llega después en la misma respuesta |
| Prerenderizado parcial | ◐, x-nextjs-postponed | Una página con parte estática y parte generada en cada petición |
| Carga útil RSC | self.__next_f | El árbol de Server Components serializado, con referencias a los Client Components |
| Hidratación | React engancha los eventos al HTML que ya existe, sin volver a crearlo | |
| Caché de página | 'use cache', x-nextjs-cache: HIT | La página entera es estática y una CDN puede guardarla |
| SPA frente a Next.js | Medición del apartado 6 | Next.js envía más JS base, pero pinta antes; lo cacheado gana en todo |
| Pages Router | pages/, getServerSideProps | El modelo original (2016); hay que saber leerlo |
| App Router | app/, page.tsx, layout.tsx | Modelo actual desde la 13.4, con Server Components |
| Cache Components | cacheComponents, 'use cache' | Caché explícita; recomendada desde 16.4 y por defecto en 17 |
| Alternativas | Vite, React Router v7, Astro, Nuxt | SPA interna, modelo HTTP, contenido, Vue |
| Active LTS | Next.js 16 | Funciones nuevas, correcciones y seguridad hasta la siguiente mayor |
| Maintenance LTS | Next.js 15 | Solo seguridad, hasta el 31-01-2027 |
| Versión del curso | Next.js 16.4 (verificado con 16.4.3) | Instala siempre el último parche 16.4.x |
| Parches de seguridad | nextjs.org/blog | Anunciados cada mes; aplícalos siempre |
| Versión instalada | next --version, next info | Comprueba también si hay un parche más reciente |
| Marcas del curso | 🆕 16.x, 🔄 16.x | Qué no tiene Next.js 15 y qué hacer en su lugar |
| Puertos | API en 3000, Next en 3001 | Evita que las dos aplicaciones choquen |