Diferencia loading, errores inesperados y recursos inexistentes, y explica cómo diseñar boundaries granulares con Suspense, retry y respuestas correctas.
Última actualización
Actualizada
Nivel
Fundamentos
loading.tsx, error.tsx y not-found.tsx modelan estados diferentes. Loading significa que una región todavía no está lista; error representa un fallo inesperado; not found representa que el recurso solicitado no existe. Mezclarlos produce respuestas engañosas y recuperación incorrecta.
La granularidad importa. Una boundary demasiado alta reemplaza toda la pantalla; una demasiado baja puede fragmentar la experiencia en múltiples skeletons y errores sin contexto.
La solicitud es válida, pero la entidad o ruta no existe. Se representa mediante notFound() y not-found.tsx.
También existen errores esperados como validación o credenciales incorrectas. Normalmente se devuelven como estado de formulario, no se lanzan para que los capture error.tsx.
Cuando el servidor ya comenzó a transmitir bytes, los headers y status están comprometidos. Una respuesta que luego muestra not found puede conservar status 200, aunque Next.js añada noindex al HTML para evitar indexación.
Si necesitas un status 404 real por cumplimiento o analítica, determina la ausencia antes de iniciar streaming.
Texto
comprobar existencia
↓
notFound antes de suspender
↓
servidor todavía puede responder 404
No muevas consultas pesadas a Proxy solo para lograr esto. Proxy debe permanecer rápido y no sustituir el acceso completo a datos.
error.tsx crea una React Error Boundary para el segmento y sus hijos. Debe ser Client Component:
TypeScript
// app/dashboard/orders/error.tsx"use client";import{ useEffect }from"react";exportdefaultfunctionOrdersError({
error,
reset,}:{
error: Error &{ digest?:string};reset:()=>void;}){useEffect(()=>{reportClientError({
message: error.message,
digest: error.digest,});},[error]);return(<section role="alert"><h2>No fue posible cargar los pedidos</h2><p>Intenta nuevamente. Si continúa, comparte el código de referencia.</p>{error.digest &&<code>{error.digest}</code>}<button type="button" onClick={reset}>
Intentar de nuevo
</button></section>);}
En desarrollo, el cliente puede recibir mensajes detallados. En producción, errores originados en Server Components suelen exponer un mensaje genérico y un digest para correlacionar con logs, evitando filtrar información sensible.
No muestres stack traces, consultas SQL ni secretos en la UI.
reset() intenta volver a renderizar el contenido de la boundary. Aporta cuando el error era transitorio o el estado externo cambió.
No garantiza:
Que se vuelva a ejecutar toda la ruta desde cero.
Que una caché sea invalidada.
Que una base de datos vuelva a estar disponible.
Que un input incorrecto se corrija.
En Next.js 16.2 existen APIs experimentales como unstable_retry y unstable_catchError para recuperación más granular o una nueva solicitud de datos. Deben marcarse como experimentales y no reemplazar el patrón estable sin decisión consciente.
// app/products/[slug]/not-found.tsximport Link from"next/link";exportdefaultfunctionProductNotFound(){return(<section><h1>Producto no encontrado</h1><p>El producto pudo eliminarse o cambiar de dirección.</p><Link href="/products">Volver al catálogo</Link></section>);}
Debe ayudar a continuar, no mostrar un mensaje genérico sin salida.
not found
→ la entidad no existe o no debe revelarse su existencia
unauthorized
→ no existe identidad autenticada válida
forbidden
→ existe identidad, pero no tiene permiso
En algunos productos se devuelve not found en lugar de forbidden para evitar revelar que una entidad privada existe. Esa es una decisión de seguridad, no una equivalencia conceptual.
Next.js ofrece convenciones de forbidden y unauthorized bajo APIs compatibles con la versión actual.
El root app/not-found.tsx puede cubrir URLs sin ruta coincidente. También existe global-not-found.tsx como capacidad experimental para aplicaciones con múltiples root layouts o un root dinámico difícil de componer.
Como omite los layouts normales, global-not-found debe producir un documento completo e importar sus estilos.
No lo enseñes como estable mientras siga bajo flag experimental.
Navegación declarativa e imperativa explica cómo el router cambia entre segmentos, precarga respuestas y preserva UI sin recargar el documento completo.