BackVolver al blog

Next.js 15.1

Next.js 15.1 introduce soporte estable para React 19, mejoras en depuración de errores, nuevas APIs experimentales de autorización y más.

Next.js 15.1 trae actualizaciones fundamentales, nuevas APIs y mejoras en la experiencia del desarrollador. Las novedades clave incluyen:

Actualiza hoy mismo, o comienza con:

Terminal
# Usa la CLI de actualización automática
npx @next/codemod@canary upgrade latest
 
# ...o actualiza manualmente
npm install next@latest react@latest react-dom@latest
 
# ...o inicia un nuevo proyecto
npx create-next-app@latest

React 19 (estable)

Next.js 15.1 ahora soporta completamente React 19:

  • Para Pages Router: Puedes usar React 19 estable sin necesidad de versiones Release Candidate o Canary, manteniendo soporte para React 18.
  • Para App Router: Continuaremos proporcionando versiones Canary de React integradas. Estas incluyen todos los cambios estables de React 19, así como características más nuevas que se validan en frameworks antes de un nuevo lanzamiento de React.

Desde el lanzamiento de Next.js 15, una adición significativa a React 19 fue el "precalentamiento de hermanos".

Para una visión completa de las actualizaciones de React 19, consulta la publicación oficial del blog de React 19.

Mejoras en depuración de errores

Hemos mejorado la depuración de errores en Next.js, asegurando que puedas localizar rápidamente el origen de los problemas, ya sea en la terminal, navegador o depuradores adjuntos. Estas mejoras aplican tanto para Webpack como para Turbopack (ahora estable con Next.js 15).

Mejoras en Source Maps

Los errores ahora son más fáciles de rastrear gracias al uso mejorado de source maps. Hemos implementado la propiedad ignoreList de source maps, que permite a Next.js ocultar frames de pila para dependencias externas, enfocándose en tu código de aplicación.

Para un mapeo de nombres de métodos más preciso, sugerimos adoptar Turbopack (ahora estable), que tiene un manejo y detección de source maps mejorado sobre Webpack.

Para autores de librerías: Recomendamos incluir la propiedad ignoreList en los sourcemaps al publicar tus librerías, especialmente si están configuradas como externas (ej. en la configuración serverExternalPackages).

Frames de pila colapsados

Hemos mejorado la lógica para colapsar frames de pila y resaltar las partes más relevantes de tu código.

  • En el navegador y overlay de errores: Los frames de dependencias de terceros están ocultos por defecto, enfocándose en tu código de aplicación. Puedes revelarlos haciendo clic en "Mostrar frames ignorados" en las herramientas de desarrollo o el overlay.
  • En la terminal: Los frames de dependencias de terceros también están colapsados por defecto, y el formato de errores ahora coincide con la salida del navegador para una experiencia consistente. Los errores se reproducen en el navegador para asegurar que no pierdas información importante durante el desarrollo.

Perfilado mejorado

Los frames ignorados también son reconocidos por los perfiles integrados del navegador. Esto facilita el perfilado de tu aplicación, permitiéndote identificar funciones lentas en tu código sin ruido de librerías externas.

Mejoras con Edge Runtime

Al usar Edge Runtime, los errores ahora se muestran consistentemente en entornos de desarrollo, asegurando una depuración fluida. Anteriormente, los errores registrados solo incluían el mensaje sin la pila.

Antes y después

Terminal Antes:

Terminal
 app/page.tsx (6:11) @ eval
 Error: boom
    at eval (./app/page.tsx:12:15)
    at Page (./app/page.tsx:11:74)
    at AsyncLocalStorage.run (node:async_hooks:346:14)
    at stringify (<anonymous>)
    at AsyncLocalStorage.run (node:async_hooks:346:14)
    at AsyncResource.runInAsyncScope (node:async_hooks:206:9)
digest: "380744807"
  4 | export default function Page() {
  5 |   const throwError = myCallback(() => {
> 6 |     throw new Error('boom')
    |           ^
  7 |   }, [])
  8 |
  9 |   throwError()
 GET / 500 in 2354ms

Terminal Después:

Terminal
 Error: boom
    at eval (app/page.tsx:6:10)
    at Page (app/page.tsx:5:32)
  4 | export default function Page() {
  5 |   const throwError = myCallback(() => {
> 6 |     throw new Error('boom')
    |          ^
  7 |   }, [])
  8 |
  9 |   throwError() {
  digest: '225828171'
}

Error Overlay Antes

Ejemplo del overlay de errores de Next.js antes de la versión 15.1

Error Overlay Después

Ejemplo del overlay de errores de Next.js después de la versión 15.1

Estas mejoras hacen que los errores sean más claros e intuitivos, permitiéndote enfocarte en construir tu aplicación en lugar de depurar.

También nos complace anunciar una nueva interfaz rediseñada para el overlay de errores, que llegará en próximas versiones.

after (estable)

La API after() es ahora estable tras su introducción en el primer RC de Next.js 15.

after() permite realizar tareas como logging, analíticas y sincronización del sistema después de que la respuesta haya terminado de transmitirse al usuario, sin bloquear la respuesta principal.

Cambios clave

Desde su introducción, hemos estabilizado after() y abordado comentarios incluyendo:

  • Mejor soporte para servidores Next.js autoalojados.
  • Corrección de bugs en escenarios donde after() interactuaba con otras características de Next.js.
  • Mayor extensibilidad, permitiendo que otras plataformas inyecten sus propios primitivos waitUntil() para potenciar after().
  • Soporte para APIs de runtime como cookies() y headers() en Server Actions y Route Handlers.
app/layout.js
import { after } from 'next/server';
import { log } from '@/app/utils';
 
export default function Layout({ children }) {
  // Tarea secundaria
  after(() => {
    log();
  });
 
  // Tarea principal
  return <>{children}</>;
}

Lee más sobre la API after y cómo aprovecharla en la documentación.

forbidden y unauthorized (experimental)

Next.js 15.1 incluye dos APIs experimentales, forbidden() y unauthorized(), basadas en comentarios de la comunidad.

Nos encantaría tu feedback — por favor pruébalo en tus entornos de desarrollo y comparte tus pensamientos en este hilo de discusión.

Resumen

Si estás familiarizado con App Router, probablemente hayas usado notFound() para activar comportamiento 404 junto con el archivo personalizable not-found.tsx. Con la versión 15.1, extendemos este enfoque a errores de autorización:

forbidden() activa un error 403 con UI personalizable vía forbidden.tsx.

unauthorized() activa un error 401 con UI personalizable vía unauthorized.tsx.

Bueno saber: Como con errores notFound(), el código de estado será 200 si el error se activa después de enviar los headers iniciales. Aprende más.

Activando la característica

Como esta característica es aún experimental, debes activarla en tu archivo next.config.ts:

next.config.ts
import type { NextConfig } from 'next';
 
const nextConfig: NextConfig = {
  experimental: {
    authInterrupts: true,
  },
};
 
export default nextConfig;

Nota: El soporte para next.config.ts se introdujo en Next.js 15. Aprende más.

Usando forbidden() y unauthorized()

Puedes usar forbidden() y unauthorized() en Server Actions, Server Components, Client Components o Route Handlers. Aquí un ejemplo:

import { verifySession } from '@/app/lib/dal';
import { forbidden } from 'next/navigation';
 
export default async function AdminPage() {
  const session = await verifySession();
 
  // Verifica si el usuario tiene rol 'admin'
  if (session.role !== 'admin') {
    forbidden();
  }
 
  // Renderiza la página admin para usuarios autorizados
  return <h1>Admin Page</h1>;
}

Creando páginas de error personalizadas

Para personalizar las páginas de error, crea los siguientes archivos:

app/forbidden.tsx
import Link from 'next/link';
 
export default function Forbidden() {
  return (
    <div>
      <h2>Prohibido</h2>
      <p>No estás autorizado para acceder a este recurso.</p>
      <Link href="/">Volver al inicio</Link>
    </div>
  );
}
app/unauthorized.tsx
import Link from 'next/link';
 
export default function Unauthorized() {
  return (
    <div>
      <h2>No autorizado</h2>
      <p>Por favor inicia sesión para acceder a esta página.</p>
      <Link href="/login">Ir a Login</Link>
    </div>
  );
}

Agradecemos a Clerk por proponer esta característica mediante un PR y ayudarnos a prototipar la API. Antes de estabilizar esta característica en 15.2, planeamos añadir más capacidades y mejoras a las APIs para soportar un rango más amplio de casos de uso.

Lee la documentación para las APIs unauthorized y forbidden para más detalles.

Otros cambios

  • [Característica] Usar ESLint 9 en create-next-app (PR)
  • [Característica] Aumentar etiquetas de caché máximas a 128 (PR)
  • [Característica] Añadir opción para desactivar CssChunkingPlugin experimental (PR)
  • [Característica] Añadir soporte experimental para CSS inlining (PR)
  • [Mejora] Silenciar advertencia Sass legacy-js-api (PR)
  • [Mejora] Corregir rechazo no manejado al usar rewrites (PR)
  • [Mejora] Asegurar que el proceso padre termine cuando falla el worker de webpack (PR)
  • [Mejora] Corregir interceptación de rutas en rutas catch-all (PR)
  • [Mejora] Corregir problema de clonación de respuesta en deduplicación de requests (PR)
  • [Mejora] Corregir redirecciones de Server Action entre múltiples root layouts (PR)
  • [Mejora] Soporte para proveer plugins MDX como strings para compatibilidad con Turbopack (PR)

Contribuidores

Next.js es el resultado del trabajo combinado de más de 3,000 desarrolladores individuales. Este lanzamiento fue posible gracias a:

Un enorme agradecimiento a @sokra, @molebox, @delbaoliveira, @eps1lon, @wbinnssmith, @JamBalaya56562, @hyungjikim, @adrian-faustino, @mottox2, @lubieowoce, @bgw, @mknichel, @wyattjoh, @huozhi, @kdy1, @mischnic, @ijjk, @icyJoseph, @acdlite, @unstubbable, @gaojude, @devjiwonchoi, @cena-ko, @lforst, @devpla, @samcx, @styfle, @ztanner, @Marukome0743, @timneutkens, @JeremieDoctrine, @ductnn, @karlhorky, @reynaldichernando, @chogyejin, @y-yagi, @philparzer, @alfawal, @Rhynden, @arlyon, @MJez29, @Goodosky, @themattmayfield, @tobySolutions, @kevinmitch14, @leerob, @emmanuelgautier, @mrhrifat, @lid0a, @boar-is, @nisabmohd, @PapatMayuri, @ovogmap, @Reflex2468, @LioRael, @betterthanhajin, @HerringtonDarkholme, @bpb54321, @ahmoin, @Kikobeats, @abdelrahmanAbouelkheir, @lumirlumir, @yeeed711, @petter, y @suu3 por su ayuda!