Cambiar tema

Next.js App Router en la práctica: grupos de rutas y layouts anidados para proyectos grandes

Easton editorial illustration: build pipeline conveyor

En el explorador de VS Code, el árbol de app muestra más de 120 carpetas apretadas. Buscas la página de gestión de usuarios del panel y pasas cinco minutos saltando entre dashboard-user-list, admin-users y backend-user-management: todas parecen la correcta.

En la revisión de código, Xiaoli añade la ruta /about y choca con la /about que marketing subió la semana pasada. Os miráis: «Tu about es sobre nosotros; el mío, sobre el producto. ¿Por qué tengo que cambiar yo?»

No es la primera vez. Al inicio había una docena de páginas y la estructura plana se veía limpia. Seis meses después, diez veces más funcionalidad y app parece un armario sin orden: sabes que está ahí, pero cada búsqueda es un revoltijo.

Si trabajas con Next.js, si el equipo supera tres personas o si pasas de 50 páginas, probablemente vivas lo mismo. La buena noticia: App Router trae cuatro herramientas para esto: grupos de rutas, layouts anidados, rutas paralelas e interceptadas. Pero la mayoría de tutoriales son demos «Hello World»; en un proyecto real sigues perdido.

En este artículo uso un e-commerce real para ver cómo aplicar las cuatro y reorganizar un directorio caótico en algo mantenible y escalable.

Dolores habituales: tres problemas de la estructura tradicional

El lío de un directorio plano

Así era nuestra estructura antes:

app/
├── page.tsx              # Inicio
├── about/page.tsx        # Sobre nosotros
├── products/page.tsx     # Listado de productos
├── product-detail/[id]/page.tsx
├── cart/page.tsx
├── checkout/page.tsx
├── dashboard/page.tsx    # Inicio del panel
├── dashboard-users/page.tsx
├── dashboard-users-active/page.tsx
├── dashboard-users-blocked/page.tsx
├── dashboard-orders/page.tsx
├── dashboard-orders-pending/page.tsx
├── dashboard-settings/page.tsx
├── auth-login/page.tsx   # Login
├── auth-register/page.tsx
└── ... (80+ carpetas más)

Duele solo mirarlo. Peor aún, las URLs quedan raras: /dashboard-users-active en lugar de /dashboard/users/active. Para evitar choques añadimos prefijos a las carpetas, pero eso solo maquilla el problema.

No distingues de un vistazo qué es público, qué es panel o qué es autenticación. Un nuevo miembro tarda días en orientarse.

Layouts duplicados y mantenimiento difícil

El front y el panel tienen layouts muy distintos: navegación y pie en el front; sidebar y permisos en el panel. Lo clásico es importar el layout en cada página:

// app/dashboard-users/page.tsx
import DashboardLayout from '@/components/DashboardLayout'

export default function UsersPage() {
  return (
    <DashboardLayout>
      <div>Contenido de gestión de usuarios</div>
    </DashboardLayout>
  )
}

Problemas: olvidas el layout en una página nueva y queda desnuda; unos usan DashboardLayout y otros AdminLayout; cambiar el sidebar del panel implica revisar 20 archivos. Cada cambio de layout da miedo.

Modales y rutas: el callejón sin salida

Producto pide: en el listado, clic en un producto abre un modal con detalle y la URL pasa a /product/123 para compartir. Suena razonable; implementarlo, un dolor.

Lo habitual: estado en cliente, modal manual, URL a mano. Código feo y, al refrescar, el modal desaparece.

Dos versiones — modal y página completa — duplican mantenimiento. Cada cambio de lógica hay que hacerlo dos veces.

La experiencia tipo Instagram — modal en el feed, página completa al refrescar — parece simple con rutas clásicas, pero no lo es.

Grupos de rutas (Route Groups): agrupar sin tocar la URL

Qué son

Paréntesis alrededor del nombre de carpeta, p. ej. (marketing): ese nombre no aparece en la URL. Mejor verlo en código:

app/
├── (marketing)/           # Grupo de marketing
│   ├── layout.tsx         # Layout del front
│   ├── page.tsx           # URL: /
│   ├── about/page.tsx     # URL: /about
│   └── products/page.tsx  # URL: /products
├── (shop)/                # Grupo e-commerce
│   ├── layout.tsx
│   ├── cart/page.tsx      # URL: /cart
│   └── checkout/page.tsx  # URL: /checkout
└── (dashboard)/           # Grupo panel
    ├── layout.tsx
    ├── dashboard/page.tsx # URL: /dashboard
    ├── users/page.tsx     # URL: /users (¡no /dashboard/users!)
    └── orders/page.tsx    # URL: /orders

(marketing), (shop) y (dashboard) no salen en la URL. (marketing)/about/page.tsx sigue siendo /about, no /marketing/about.

¿Para qué entonces? El valor está en organizar código, no en cambiar rutas. Agrupas por negocio, equipo o módulo; la estructura se lee clara y la URL sigue corta.

Caso práctico: un grupo por equipo

Tres equipos: marketing (web y landing), producto (tienda), backend (admin). Antes, todos en el mismo app y conflictos constantes. Con grupos:

app/
├── (team-marketing)/      # Equipo marketing
│   ├── layout.tsx
│   ├── page.tsx           # Inicio
│   ├── about/page.tsx
│   └── pricing/page.tsx
├── (team-product)/        # Equipo producto
│   ├── layout.tsx
│   ├── products/page.tsx
│   └── product/[id]/page.tsx
└── (team-backend)/        # Equipo backend
    ├── layout.tsx
    ├── dashboard/page.tsx
    └── admin/page.tsx

Ventajas:

  1. Menos conflictos de archivos. Cada equipo en su carpeta; menos choques en Git.
  2. PRs más claros. Ves de inmediato qué equipo toca el cambio.
  3. Layouts independientes. Cada grupo con su layout.tsx sin importarlo página a página.

Otro caso: agrupar por tipo de layout

app/
├── (with-nav)/            # Páginas con barra superior
│   ├── layout.tsx
│   ├── page.tsx
│   ├── about/page.tsx
│   └── products/page.tsx
├── (fullscreen)/          # Pantalla completa
│   ├── layout.tsx
│   └── video/[id]/page.tsx
└── (auth)/                # Auth (layout mínimo)
    ├── layout.tsx
    ├── login/page.tsx
    └── register/page.tsx

Login y registro sin nav ni pie; vídeo a pantalla completa en su grupo.

Precauciones

Dos grupos no pueden resolver la misma ruta. No puedes tener (marketing)/about/page.tsx y (shop)/about/page.tsx: ambos son /about y Next.js falla.

Planifica rutas únicas o renombra, p. ej. (shop)/about-us/page.tsx.

Nombra grupos con sentido: (marketing), (dashboard), (auth), no (group1).

Layouts anidados (Nested Layouts): herencia automática

Cómo funcionan

Los grupos ordenan carpetas; falta la jerarquía de layouts. En un panel suele haber:

  • Nivel 1: barra superior + sidebar (todas las páginas del panel)
  • Nivel 2: pestañas del módulo usuarios (activos, bloqueados)
  • Nivel 3: contenido de la página

Coloca layout.tsx en cada nivel y se anidan solos:

app/(dashboard)/
├── layout.tsx              # Nivel 1: nav + sidebar
├── users/
│   ├── layout.tsx          # Nivel 2: pestañas usuarios
│   ├── active/page.tsx     # /users/active
│   └── blocked/page.tsx    # /users/blocked
└── orders/
    ├── layout.tsx          # Nivel 2: pestañas pedidos
    ├── pending/page.tsx
    └── completed/page.tsx

En /users/active:

DashboardLayout (nivel 1)
  └─ UsersLayout (nivel 2)
      └─ ActiveUsersPage (página)

Código:

// app/(dashboard)/layout.tsx - Nivel 1
export default function DashboardLayout({
  children
}: {
  children: React.ReactNode
}) {
  return (
    <div className="dashboard-container">
      <TopBar />
      <div className="content-area">
        <Sidebar />
        <main>{children}</main>
      </div>
    </div>
  )
}

// app/(dashboard)/users/layout.tsx - Nivel 2
export default function UsersLayout({
  children
}: {
  children: React.ReactNode
}) {
  return (
    <div className="users-section">
      <div className="tabs">
        <Link href="/users/active">Usuarios activos</Link>
        <Link href="/users/blocked">Usuarios bloqueados</Link>
      </div>
      {children}
    </div>
  )
}

// app/(dashboard)/users/active/page.tsx - Página
export default function ActiveUsersPage() {
  return <div>Lista de usuarios activos...</div>
}

La página no importa layouts: Next.js los anida.

Ventaja de renderizado parcial

Al pasar de «activos» a «bloqueados»:

  • El layout de nivel 1 (nav, sidebar) no se re-renderiza
  • El de nivel 2 (pestañas) tampoco
  • Solo cambia el contenido de la página

Resultado: mejor rendimiento y estado en cliente conservado (p. ej. búsqueda en el sidebar no se borra al cambiar de ruta).

Caso: navegación multinivel

app/(dashboard)/
├── layout.tsx              # Nivel 1: barra + sidebar
├── users/
│   ├── layout.tsx          # Nivel 2: zona usuarios
│   ├── active/page.tsx     # Nivel 3
│   └── blocked/page.tsx
└── orders/
    ├── layout.tsx          # Nivel 2: zona pedidos
    ├── pending/page.tsx
    └── completed/page.tsx

Consejos de rendimiento

Los layouts son Server Components por defecto. Si hay interacción (búsqueda, menús), extrae un Client Component:

// app/(dashboard)/layout.tsx - Server Component
import SearchBar from '@/components/SearchBar' // Client Component

export default function DashboardLayout({ children }) {
  return (
    <div>
      <SearchBar />
      <main>{children}</main>
    </div>
  )
}

// components/SearchBar.tsx
'use client'
import { useState } from 'react'

export default function SearchBar() {
  const [query, setQuery] = useState('')
  // ...lógica de interacción
}

Añade loading.tsx por nivel para estados de carga independientes:

app/(dashboard)/
├── layout.tsx
├── loading.tsx
└── users/
    ├── layout.tsx
    ├── loading.tsx
    └── active/
        ├── page.tsx
        └── loading.tsx

Rutas paralelas (Parallel Routes): varias páginas a la vez

Qué resuelven

Un dashboard suele mostrar módulos independientes: analítica, equipo, notificaciones. Cada uno con datos y tiempos distintos. En una sola página, un panel lento bloquea todo.

Las rutas paralelas dividen slots (@) con loading y error propios.

Sintaxis básica

Carpetas que empiezan por @:

app/dashboard/
├── layout.tsx
├── @analytics/page.tsx
├── @team/page.tsx
├── @notifications/page.tsx
└── page.tsx

En layout.tsx recibes los slots como props:

// app/dashboard/layout.tsx
export default function DashboardLayout({
  children,
  analytics,
  team,
  notifications,
}: {
  children: React.ReactNode
  analytics: React.ReactNode
  team: React.ReactNode
  notifications: React.ReactNode
}) {
  return (
    <div className="dashboard-grid">
      <div className="main-content">{children}</div>
      <div className="top-panels">
        <div className="panel">{analytics}</div>
        <div className="panel">{team}</div>
      </div>
      <div className="bottom-panel">{notifications}</div>
    </div>
  )
}

Cada slot con su loading.tsx y error.tsx:

app/dashboard/
├── @analytics/
│   ├── page.tsx
│   ├── loading.tsx
│   └── error.tsx
├── @team/
│   ├── page.tsx
│   ├── loading.tsx
│   └── error.tsx
└── @notifications/
    ├── page.tsx
    ├── loading.tsx
    └── error.tsx

Si analítica tarda, solo ese panel muestra carga; un error no tumba la página entera.

Renderizado condicional

Solo admins ven el panel de equipo:

// app/dashboard/layout.tsx
import { auth } from '@/lib/auth'

export default async function DashboardLayout({
  analytics,
  team,
  notifications,
}) {
  const user = await auth()
  const isAdmin = user?.role === 'admin'

  return (
    <div className="dashboard-grid">
      <div>{analytics}</div>
      {isAdmin && <div>{team}</div>}
      <div>{notifications}</div>
    </div>
  )
}

Rol de default.tsx

Al ir de /dashboard a /dashboard/settings, un slot puede no tener página. default.tsx evita el error:

// app/dashboard/@team/default.tsx
export default function Default() {
  return null
}

Cuándo usarlas

Menos universal que grupos y layouts anidados. Encajan en:

  • Dashboard multipanel con carga independiente
  • A/B testing por slot
  • Permisos que ocultan módulos

Si solo apilas contenido estático, basta un page.tsx.

Rutas interceptadas (Intercepting Routes): modales con URL

Experiencia tipo Instagram

Es la más difícil de las cuatro. En Instagram, clic en una foto del feed: modal con zoom y URL /photo/abc123.

  • Refrescar → página completa, sin modal
  • Compartir URL → página completa para quien abre el enlace
  • Cerrar → vuelves al feed

URL compartible, contexto al refrescar y navegación fluida. Difícil sin interceptación.

Sintaxis

  • (.) mismo nivel
  • (..) nivel superior
  • (..)(..) dos niveles arriba
  • (...) desde la raíz
app/
├── products/
│   ├── page.tsx                    # Listado
│   └── (..)product/[id]/page.tsx   # Intercepta /product/123 como modal
└── product/
    └── [id]/page.tsx               # Página completa

En /products, <Link href="/product/123">:

  • Navegación cliente: intercepta → modal
  • URL directa o refresh: página completa

Caso: modal de detalle de producto

app/
├── (shop)/
│   └── products/
│       ├── page.tsx
│       └── (..)product/[id]/page.tsx
└── product/
    └── [id]/page.tsx

Modal:

// app/(shop)/products/(..)product/[id]/page.tsx
'use client'
import { useRouter } from 'next/navigation'
import Modal from '@/components/Modal'
import ProductDetail from '@/components/ProductDetail'

export default function ProductModal({
  params
}: {
  params: { id: string }
}) {
  const router = useRouter()

  return (
    <Modal onClose={() => router.back()}>
      <ProductDetail id={params.id} />
    </Modal>
  )
}

Página completa:

// app/product/[id]/page.tsx
import ProductDetail from '@/components/ProductDetail'

export default function ProductPage({
  params
}: {
  params: { id: string }
}) {
  return (
    <div className="product-page">
      <ProductDetail id={params.id} />
    </div>
  )
}

ProductDetail se reutiliza; cambia el contenedor (modal vs página).

Con rutas paralelas

app/(shop)/products/
├── layout.tsx
├── page.tsx
├── @modal/
│   ├── (..)product/[id]/page.tsx
│   └── default.tsx
// app/(shop)/products/layout.tsx
export default function ProductsLayout({
  children,
  modal,
}: {
  children: React.ReactNode
  modal: React.ReactNode
}) {
  return (
    <>
      {children}
      {modal}
    </>
  )
}

// app/(shop)/products/@modal/default.tsx
export default function Default() {
  return null
}

Modal y contenido principal separados; estado más claro.

Precauciones

  1. Solo en navegación cliente. URL directa o F5 → no intercepta.
  2. Dos versiones que mantener (aunque compartas componentes).
  3. Coincidencia por ruta URL, no por carpetas. Los grupos no cambian la URL; (..) desde /products apunta a /product/[id].

Cuándo usarlas

Encajan en galerías, detalle de producto en modal, login en modal con /login accesible directo.

No las necesitas para popups sin URL ni sin deep linking.

Caso integrado: estructura completa de e-commerce

Requisitos

Front (usuario):

  • Inicio, about (marketing)
  • Productos, detalle (con modal)
  • Carrito, checkout

Panel (admin):

  • Dashboard (analítica, equipo, notificaciones)
  • Usuarios (activos, bloqueados)
  • Pedidos (pendientes, completados)

Auth:

  • Login, registro (layout sin nav)

Estructura final

app/
├── layout.tsx                          # Layout raíz

├── (marketing)/                        # Grupo marketing
│   ├── layout.tsx
│   ├── page.tsx                        # /
│   ├── about/page.tsx                  # /about
│   └── pricing/page.tsx                # /pricing

├── (shop)/                             # Grupo tienda
│   ├── layout.tsx
│   ├── products/
│   │   ├── layout.tsx
│   │   ├── page.tsx                    # /products
│   │   └── @modal/
│   │       ├── (..)product/[id]/page.tsx
│   │       └── default.tsx
│   ├── cart/page.tsx                   # /cart
│   └── checkout/page.tsx               # /checkout

├── product/
│   └── [id]/page.tsx                   # /product/123 (página completa)

├── (dashboard)/                        # Grupo panel
│   ├── layout.tsx
│   ├── dashboard/
│   │   ├── layout.tsx
│   │   ├── page.tsx                    # /dashboard
│   │   ├── @analytics/
│   │   │   ├── page.tsx
│   │   │   ├── loading.tsx
│   │   │   └── default.tsx
│   │   ├── @team/
│   │   │   ├── page.tsx
│   │   │   ├── loading.tsx
│   │   │   └── default.tsx
│   │   └── @notifications/
│   │       ├── page.tsx
│   │       ├── loading.tsx
│   │       └── default.tsx
│   ├── users/
│   │   ├── layout.tsx
│   │   ├── active/page.tsx             # /users/active
│   │   └── blocked/page.tsx            # /users/blocked
│   └── orders/
│       ├── layout.tsx
│       ├── pending/page.tsx            # /orders/pending
│       └── completed/page.tsx          # /orders/completed

└── (auth)/                             # Grupo auth
    ├── layout.tsx
    ├── login/page.tsx                  # /login
    └── register/page.tsx               # /register

Comparación con estructura plana

DimensiónEstructura planaGrupos + layouts anidados
Buscar archivos100+ archivos, prefijos confusosAgrupado por módulo, claro
LayoutsImport manual en cada páginaHerencia automática
EquipoUn solo directorio, muchos conflictosCarpetas por equipo/módulo
URLsPrefijos (dashboard-users-active)URLs limpias (/users/active)
ModalesEstado cliente, se pierde al refrescarRutas, refresh → página completa
RendimientoRe-render de layoutsRender parcial

Beneficios reales

  1. ~50 % más rápido encontrar páginas.
  2. Un solo layout.tsx para cambiar el sidebar del panel.
  3. ~60 % menos conflictos Git entre equipos.
  4. Onboarding más rápido para nuevos miembros.

Consejos prácticos

  1. No reestructures todo de golpe. Empieza por el panel.
  2. Nombres claros: (marketing), (shop), (dashboard), (auth).
  3. Documenta la estructura en el README.
  4. TypeScript con alias de rutas:
// tsconfig.json
{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"],
      "@/components/*": ["./src/components/*"],
      "@/app/*": ["./src/app/*"]
    }
  }
}

Buenas prácticas y precauciones

Convención de nombres de grupos

Recomendado:

  • (marketing) — marketing
  • (dashboard) o (admin) — panel
  • (auth) — autenticación
  • (team-xxx) — por equipo
  • (feature-xxx) — por función

Evitar:

  • (group1), (group2)
  • (temp), (test)
  • Nombres largos como (marketing-and-sales-pages)

El nombre es para desarrolladores; que sea obvio.

Evitar conflictos de rutas

❌ Incorrecto:
app/
├── (marketing)/about/page.tsx  # URL: /about
└── (shop)/about/page.tsx       # URL: /about — conflicto

Soluciones: mapa de rutas único, prefijos distintos o niveles diferentes.

Cuándo usar rutas paralelas

:

  • Paneles con datos independientes
  • Loading por módulo
  • Permisos condicionales
  • A/B por slot

No:

  • Contenido apilado simple
  • Sin loading independiente
  • Zonas estáticas

Si dudas, probablemente no las necesitas.

Límites de rutas interceptadas

  1. Solo navegación cliente.
  2. Dos versiones que mantener.
  3. (..) sigue la URL, no la carpeta del sistema de archivos.

Sin URL compartible, un modal cliente basta.

Optimización de rendimiento

  1. Layouts como Server Components; cliente solo donde haga falta.
  2. loading.tsx por nivel.
  3. Suspense con loading para streaming fino.
  4. No más de ~4 niveles de layout anidado.

Estrategia de migración

Desde Pages Router:

  1. Migración incremental (app y pages coexisten).
  2. Primero layouts con grupos y anidación.
  3. Después datos: getServerSidePropsfetch, etc.
  4. Rutas: getStaticPathsgenerateStaticParams.
  5. Feature flags para rollback.

Colaboración en equipo

  1. Norma de estructura en README/Wiki.
  2. En PR: conflictos de rutas y anidación correcta.
  3. ESLint para convenciones de carpetas.
  4. Refactor periódico de rutas obsoletas.

Depuración

  1. React DevTools — árbol de layouts.
  2. console.log en layouts — ¿re-render en cada navegación?
  3. Network — SSR vs cliente.
  4. Terminal Next.js — avisos de conflicto y layouts.

Conclusión

La escena del inicio: 120 carpetas, cinco minutos para encontrar una página.

Si tu proyecto Next.js vive esto, estas cuatro piezas ayudan:

Grupos de rutas — organización por negocio o equipo sin alargar URLs.

Layouts anidados — herencia automática; un cambio, todas las páginas del nivel.

Rutas paralelas — módulos independientes con loading y error propios.

Rutas interceptadas — modales con URL compartible y página completa al refrescar.

Empieza pequeño: un módulo piloto, valida y extiende. Migración incremental, riesgo controlado.

Abajo tienes la plantilla completa del e-commerce para copiar o adaptar. Si te queda alguna duda, comenta.

¡Que tu app deje de ser un caos y sea un placer mantenerlo!

Flujo completo para reestructurar directorios en proyectos Next.js grandes

Reorganiza una estructura caótica con grupos de rutas, layouts anidados, rutas paralelas e interceptadas

⏱️ Estimated time: 8 hr

  1. 1

    Step 1: Analizar la estructura actual

    Evalúa los problemas actuales:
    • Cuenta carpetas (más de 50 → conviene reestructurar)
    • Identifica conflictos de rutas
    • Localiza layouts duplicados
    • Registra los dolores del equipo

    Identifica zonas funcionales:
    • Marketing (inicio, about, pricing)
    • Tienda (productos, carrito, pedidos)
    • Panel de administración (usuarios, pedidos, ajustes)
  2. 2

    Step 2: Crear grupos de rutas por zona funcional

    Usa paréntesis para crear grupos:
    • (marketing): páginas de marketing
    • (shop): páginas de la tienda
    • (dashboard): panel de administración

    Notas:
    • El nombre del grupo no afecta la URL
    • La misma URL no puede estar en varios grupos
    • Cada grupo puede tener su propio layout.js
  3. 3

    Step 3: Diseñar la jerarquía de layouts anidados

    Diseña según la jerarquía de UI:
    • Nivel 1: app/layout.js (global)
    • Nivel 2: layout.js de zona (p. ej. shop/layout.js)
    • Nivel 3: layout.js de detalle (p. ej. shop/products/[id]/layout.js)

    Puntos clave:
    • Cada nivel solo añade UI propia de ese nivel
    • Los layouts hijos heredan del padre
    • Al cambiar de página, el layout no se vuelve a renderizar
  4. 4

    Step 4: Implementar rutas paralelas (si hace falta)

    Crea slots con @:
    • @modal: slot de modal
    • @sales, @orders: módulos independientes del panel

    Recíbelos en layout.js:
    • export default function Layout({ children, modal })
    • En JSX: {modal}

    Cada slot puede tener su loading.js y error.js
  5. 5

    Step 5: Implementar rutas interceptadas (si necesitas modales)

    Crea rutas interceptadas:
    • En @modal: (.)photos/[id]/page.js
    • photos/[id]/page.js (página completa)

    Sintaxis:
    • (.): intercepta rutas del mismo nivel
    • (..): intercepta el nivel superior
    • (...): intercepta desde la raíz

    Debes crear default.js que devuelva null
  6. 6

    Step 6: Probar y validar

    Comprueba:
    • Todas las rutas funcionan
    • Los layouts anidan correctamente
    • Las transiciones son fluidas
    • Los modales se comportan bien

    Herramientas de depuración:
    • React DevTools para el árbol de componentes
    • console.log para contar renders
    • Pestaña Network para peticiones
    • Salida de terminal de Next.js para avisos

FAQ

¿Cuándo debería usar grupos de rutas?
Cuando el proyecto supera unas 50 carpetas o necesitas layouts raíz distintos por zona funcional. Los grupos de rutas encajan muy bien en proyectos con varios equipos: reducen conflictos en Git y dejan la estructura más clara.
¿Los grupos de rutas afectan la URL?
No. Se nombran con paréntesis (p. ej. (marketing)); Next.js ignora el nombre entre paréntesis y la URL no cambia. Por ejemplo, (marketing)/about/page.js sigue siendo /about, no /marketing/about.
¿Los layouts anidados afectan el rendimiento?
No; de hecho suelen mejorarlo. En Next.js, al cambiar subrutas los layouts anidados no se vuelven a renderizar: solo se recarga el page.js más interno. Puedes conservar estado en el layout (p. ej. scroll del sidebar) al navegar.
¿En qué se diferencian las rutas paralelas de un componente normal?
Cada slot de ruta paralela es un fragmento de página independiente con su loading.js y error.js, cargando en paralelo sin bloquearse. Un componente normal espera a que carguen todos los datos y un fallo puede tumbar toda la página.
¿Cómo elegir la sintaxis de rutas interceptadas?
(.) intercepta rutas del mismo nivel (p. ej. @modal y photos bajo app), (..) el nivel superior y (...) desde la raíz. Si dudas, prueba primero (...) y, cuando funcione, ajusta según la estructura de carpetas.
¿Cómo evitar conflictos de rutas?
Agrupa por función con grupos de rutas y asegura que cada URL sea única. Si necesitas rutas con el mismo nombre, añade un segmento real en la URL (sin paréntesis) o renombra una de las rutas.
¿Cuánto tarda reestructurar un proyecto grande?
Depende del tamaño. Un proyecto mediano (50-100 páginas) puede llevar 1-2 semanas; uno grande, hasta un mes. Empieza por un módulo piloto, valida y extiende; la migración incremental reduce el riesgo.

12 min de lectura · Publicado el: 18 dic 2025 · Actualizado el: 21 ago 2026

Comentarios

Inicia sesión con GitHub para dejar un comentario

Easton BlogEaston Blog