Rutas paralelas
Las rutas paralelas te permiten renderizar simultáneamente o condicionalmente una o más páginas dentro del mismo diseño. Son útiles para secciones altamente dinámicas de una aplicación, como paneles de control y feeds en sitios sociales.
Por ejemplo, considerando un panel de control, puedes usar rutas paralelas para renderizar simultáneamente las páginas team
y analytics
:

Slots
Las rutas paralelas se crean usando slots con nombre. Los slots se definen con la convención @folder
. Por ejemplo, la siguiente estructura de archivos define dos slots: @analytics
y @team
:

Los slots se pasan como props al diseño padre compartido. Para el ejemplo anterior, el componente en app/layout.js
ahora acepta los props de slots @analytics
y @team
, y puede renderizarlos en paralelo junto con el prop children
:
export default function Layout({
children,
team,
analytics,
}: {
children: React.ReactNode
analytics: React.ReactNode
team: React.ReactNode
}) {
return (
<>
{children}
{team}
{analytics}
</>
)
}
export default function Layout({ children, team, analytics }) {
return (
<>
{children}
{team}
{analytics}
</>
)
}
Sin embargo, los slots no son segmentos de ruta y no afectan la estructura de la URL. Por ejemplo, para /@analytics/views
, la URL será /views
ya que @analytics
es un slot.
Nota importante:
- El prop
children
es un slot implícito que no necesita mapearse a una carpeta. Esto significa queapp/page.js
es equivalente aapp/@children/page.js
.
Estado activo y navegación
Por defecto, Next.js realiza un seguimiento del estado activo (o subpágina) para cada slot. Sin embargo, el contenido renderizado dentro de un slot dependerá del tipo de navegación:
- Navegación suave: Durante la navegación del lado del cliente, Next.js realizará un renderizado parcial, cambiando la subpágina dentro del slot, mientras mantiene las subpáginas activas de los otros slots, incluso si no coinciden con la URL actual.
- Navegación dura: Después de una carga completa de página (actualización del navegador), Next.js no puede determinar el estado activo para los slots que no coinciden con la URL actual. En su lugar, renderizará un archivo
default.js
para los slots no coincidentes, o404
sidefault.js
no existe.
Nota importante:
- El
404
para rutas no coincidentes ayuda a garantizar que no renderices accidentalmente una ruta paralela en una página para la que no estaba destinada.
default.js
Puedes definir un archivo default.js
para renderizar como respaldo para slots no coincidentes durante la carga inicial o recarga completa de página.
Considera la siguiente estructura de carpetas. El slot @team
tiene una página /settings
, pero @analytics
no.

Al navegar a /settings
, el slot @team
renderizará la página /settings
mientras mantiene la página activa actual para el slot @analytics
.
Al actualizar, Next.js renderizará un default.js
para @analytics
. Si default.js
no existe, se renderizará un 404
.
Además, como children
es un slot implícito, también necesitas crear un archivo default.js
para renderizar un respaldo para children
cuando Next.js no pueda recuperar el estado activo de la página padre.
useSelectedLayoutSegment(s)
Tanto useSelectedLayoutSegment
como useSelectedLayoutSegments
aceptan un parámetro parallelRoutesKey
, que te permite leer el segmento de ruta activo dentro de un slot.
'use client'
import { useSelectedLayoutSegment } from 'next/navigation'
export default function Layout({ auth }: { auth: React.ReactNode }) {
const loginSegment = useSelectedLayoutSegment('auth')
// ...
}
'use client'
import { useSelectedLayoutSegment } from 'next/navigation'
export default function Layout({ auth }) {
const loginSegment = useSelectedLayoutSegment('auth')
// ...
}
Cuando un usuario navega a app/@auth/login
(o /login
en la barra de URL), loginSegment
será igual a la cadena "login"
.
Ejemplos
Rutas condicionales
Puedes usar rutas paralelas para renderizar condicionalmente rutas basadas en ciertas condiciones, como el rol del usuario. Por ejemplo, para renderizar una página de panel de control diferente para los roles /admin
o /user
:

import { checkUserRole } from '@/lib/auth'
export default function Layout({
user,
admin,
}: {
user: React.ReactNode
admin: React.ReactNode
}) {
const role = checkUserRole()
return <>{role === 'admin' ? admin : user}</>
}
import { checkUserRole } from '@/lib/auth'
export default function Layout({ user, admin }) {
const role = checkUserRole()
return <>{role === 'admin' ? admin : user}</>
}
Grupos de pestañas
Puedes agregar un layout
dentro de un slot para permitir que los usuarios naveguen el slot de forma independiente. Esto es útil para crear pestañas.
Por ejemplo, el slot @analytics
tiene dos subpáginas: /page-views
y /visitors
.

Dentro de @analytics
, crea un archivo layout
para compartir las pestañas entre las dos páginas:
import Link from 'next/link'
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<>
<nav>
<Link href="/page-views">Page Views</Link>
<Link href="/visitors">Visitors</Link>
</nav>
<div>{children}</div>
</>
)
}
import Link from 'next/link'
export default function Layout({ children }) {
return (
<>
<nav>
<Link href="/page-views">Page Views</Link>
<Link href="/visitors">Visitors</Link>
</nav>
<div>{children}</div>
</>
)
}
Modales
Las rutas paralelas pueden usarse junto con Rutas de interceptación para crear modales. Esto te permite resolver desafíos comunes al construir modales, como:
- Hacer que el contenido del modal sea compartible a través de una URL.
- Preservar el contexto cuando se actualiza la página, en lugar de cerrar el modal.
- Cerrar el modal en la navegación hacia atrás en lugar de ir a la ruta anterior.
- Reabrir el modal en la navegación hacia adelante.
Considera el siguiente patrón de UI, donde un usuario puede abrir un modal de inicio de sesión desde un diseño usando navegación del lado del cliente, o acceder a una página /login
separada:

Para implementar este patrón, comienza creando una ruta /login
que renderice tu página principal de inicio de sesión.

import { Login } from '@/app/ui/login'
export default function Page() {
return <Login />
}
import { Login } from '@/app/ui/login'
export default function Page() {
return <Login />
}
Luego, dentro del slot @auth
, agrega un archivo default.js
que devuelva null
. Esto asegura que el modal no se renderice cuando no está activo.
export default function Default() {
return null
}
export default function Default() {
return null
}
Dentro de tu slot @auth
, intercepta la ruta /login
actualizando la carpeta /(.)login
. Importa el componente <Modal>
y sus hijos al archivo /(.)login/page.tsx
:
import { Modal } from '@/app/ui/modal'
import { Login } from '@/app/ui/login'
export default function Page() {
return (
<Modal>
<Login />
</Modal>
)
}
import { Modal } from '@/app/ui/modal'
import { Login } from '@/app/ui/login'
export default function Page() {
return (
<Modal>
<Login />
</Modal>
)
}
Nota importante:
- La convención usada para interceptar la ruta, ej.
(.)
, depende de la estructura de tu sistema de archivos. Consulta Convención de Rutas de Interceptación.- Al separar la funcionalidad del
<Modal>
del contenido del modal (<Login>
), puedes asegurar que cualquier contenido dentro del modal, ej. formularios, sean Componentes de Servidor. Consulta Intercalar Componentes de Cliente y Servidor para más información.
Abriendo el modal
Ahora, puedes aprovechar el enrutador de Next.js para abrir y cerrar el modal. Esto asegura que la URL se actualice correctamente cuando el modal está abierto, y al navegar hacia atrás y adelante.
Para abrir el modal, pasa el slot @auth
como prop al diseño padre y renderízalo junto con el prop children
.
import Link from 'next/link'
export default function Layout({
auth,
children,
}: {
auth: React.ReactNode
children: React.ReactNode
}) {
return (
<>
<nav>
<Link href="/login">Abrir modal</Link>
</nav>
<div>{auth}</div>
<div>{children}</div>
</>
)
}
import Link from 'next/link'
export default function Layout({ auth, children }) {
return (
<>
<nav>
<Link href="/login">Abrir modal</Link>
</nav>
<div>{auth}</div>
<div>{children}</div>
</>
)
}
Cuando el usuario haga clic en el <Link>
, el modal se abrirá en lugar de navegar a la página /login
. Sin embargo, al actualizar o cargar inicialmente, navegar a /login
llevará al usuario a la página principal de inicio de sesión.
Cerrando el modal
Puedes cerrar el modal llamando a router.back()
o usando el componente Link
.
'use client'
import { useRouter } from 'next/navigation'
export function Modal({ children }: { children: React.ReactNode }) {
const router = useRouter()
return (
<>
<button
onClick={() => {
router.back()
}}
>
Cerrar modal
</button>
<div>{children}</div>
</>
)
}
'use client'
import { useRouter } from 'next/navigation'
export function Modal({ children }) {
const router = useRouter()
return (
<>
<button
onClick={() => {
router.back()
}}
>
Cerrar modal
</button>
<div>{children}</div>
</>
)
}
Cuando usas el componente Link
para navegar fuera de una página que no debería renderizar el slot @auth
más, usamos una ruta catch-all que devuelve null
.
import Link from 'next/link'
export function Modal({ children }: { children: React.ReactNode }) {
return (
<>
<Link href="/">Cerrar modal</Link>
<div>{children}</div>
</>
)
}
import Link from 'next/link'
export function Modal({ children }) {
return (
<>
<Link href="/">Cerrar modal</Link>
<div>{children}</div>
</>
)
}
export default function CatchAll() {
return null
}
export default function CatchAll() {
return null
}
Nota importante:
- Usamos una ruta catch-all en nuestro slot
@auth
para cerrar el modal debido al comportamiento descrito en Estado activo y navegación. Dado que las navegaciones del lado del cliente a una ruta que ya no coincide con el slot permanecerán visibles, necesitamos hacer coincidir el slot con una ruta que devuelvanull
para cerrar el modal.- Otros ejemplos podrían incluir abrir un modal de foto en una galería mientras también tienes una página dedicada
/photo/[id]
, o abrir un carrito de compras en un modal lateral.- Ver un ejemplo de modales con Rutas Interceptadas y Paralelas.
UI de carga y error
Las rutas paralelas pueden transmitirse de forma independiente, permitiéndote definir estados de error y carga independientes para cada ruta:

Consulta la documentación de UI de carga y Manejo de errores para más información.
Rutas dinámicas
Las rutas dinámicas pueden usarse para generar segmentos de ruta programáticamente desde datos dinámicos.
Rutas de intercepción
Utiliza rutas de intercepción para cargar una nueva ruta dentro del diseño actual mientras se enmascara la URL del navegador, útil para patrones de enrutamiento avanzados como modales.