Busca en todas las páginas de la documentación
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Estas recetas de skills están diseñadas para Claude Code, pero también funcionan con otros agentes de codificación con IA que admitan archivos de skill/instrucciones.
El contenido completo de SKILL.md que puedes copiar en .claude/skills/nextjs-routing/SKILL.md:
---
name: nextjs-routing-deep-dive
description: "Patrones avanzados de routing en App Router, rutas paralelas, rutas interceptadas y layouts. Úsalo cuando te pidan: ayuda con routing, rutas paralelas, rutas interceptadas, patrón modal, grupos de rutas, rutas dinámicas, middleware, rutas catch-all."
allowed-tools: "Read, Write, Edit, Glob, Grep, Bash(npm:*), Bash(npx:*), Agent"
---
# Profundización en el routing de Next.js
Eres un experto en routing del App Router de Next.js. Proporciona orientación autorizada sobre cada patrón de routing disponible en App Router.
## Convenciones de archivos
| Archivo | Propósito | Renderiza |
|------|---------|---------|
| layout.tsx | Contenedor de UI compartido; preserva el state en la navegación | Envuelve children |
| template.tsx | Como layout, pero se vuelve a montar en cada navegación | Envuelve children |
| page.tsx | UI única de la ruta; hace la ruta accesible públicamente | Contenido de la ruta |
| loading.tsx | UI de carga instantánea mediante Suspense | Mientras la página carga |
| error.tsx | Error boundary del segmento | Ante un error |
| not-found.tsx | UI 404 del segmento | Al llamar notFound() |
| default.tsx | Alternativa para slots de rutas paralelas sin coincidencia | Fallback del slot |
| route.ts | Endpoint de API (GET, POST, etc.) | JSON o Response |
| global-error.tsx | Error boundary a nivel raíz | Ante un error raíz |
## Tipos de rutas
### Rutas estáticasapp/about/page.tsx -> /about app/blog/page.tsx -> /blog app/dashboard/settings/page.tsx -> /dashboard/settings
### Rutas dinámicas
app/blog/[slug]/page.tsx -> /blog/my-post (un solo parámetro) app/shop/[...slug]/page.tsx -> /shop/a/b/c (catch-all, obligatorio) app/docs/[[...slug]]/page.tsx -> /docs o /docs/a/b (catch-all opcional)
### Grupos de rutas
app/(marketing)/about/page.tsx -> /about (sin /marketing en la URL) app/(shop)/cart/page.tsx -> /cart app/(auth)/login/layout.tsx -> layout separado para páginas de auth
Los grupos de rutas con paréntesis NO afectan la URL. Úsalos para:
- Organizar rutas por ámbito sin cambiar la estructura de la URL
- Aplicar distintos layouts a diferentes grupos de rutas
- Dividir el layout raíz en múltiples layouts raíz
### Rutas paralelas
app/@modal/default.tsx app/@modal/(.)photo/[id]/page.tsx app/layout.tsx -> recibe props { children, modal } app/photo/[id]/page.tsx -> destino de navegación directa
Reglas para rutas paralelas:
- Se definen con la convención @folder
- Cada slot se pasa como prop al layout padre
- Los slots se renderizan de forma independiente y pueden tener su propio loading/error
- SIEMPRE crea un default.tsx por cada slot (evita 404 al actualizar la página)
- Los slots NO afectan la estructura de la URL
### Rutas interceptadas
(.)folder -> intercepta en el mismo nivel (..)folder -> intercepta un nivel arriba (..)(..)folder -> intercepta dos niveles arriba (...)folder -> intercepta desde la raíz
Patrón habitual: interception de modal:
app/@modal/(.)photo/[id]/page.tsx -> la navegación suave muestra el modal app/photo/[id]/page.tsx -> la navegación directa muestra la página completa
## Árbol de decisiones: elegir un patrón de routing
1. **¿Necesitas un contenedor compartido que persista entre navegaciones?**
- Sí -> Usa layout.tsx
- Sí, pero debe volver a montarse -> Usa template.tsx
2. **¿Necesitas varias secciones independientes en la misma página?**
- Sí -> Usa rutas paralelas (@slot)
- Cada sección carga de forma independiente -> Añade loading.tsx por slot
3. **¿Necesitas un modal que se abra en navegación suave pero muestre una página completa en navegación directa?**
- Sí -> Usa rutas interceptadas + rutas paralelas
- Crea el slot @modal con ruta interceptada (.)
4. **¿Necesitas layouts distintos para distintas secciones?**
- Sí -> Usa grupos de rutas: (marketing), (dashboard), (auth)
5. **¿Necesitas segmentos dinámicos en la URL?**
- Un solo parámetro -> [param]
- Varios segmentos -> [...params] o [[...params]]
6. **¿Necesitas proteger rutas?**
- Redirecciones simples -> middleware.ts
- Lógica de auth compleja -> layout.tsx con comprobación de auth
- Protección de API -> route.ts con validación de auth
## Middleware
```typescript
// middleware.ts (raíz del proyecto o src/)
import \{ NextResponse \} from "next/server";
import type \{ NextRequest \} from "next/server";
export function middleware(request: NextRequest) \{
// Comprobar autenticación
const token = request.cookies.get("session");
if (!token && request.nextUrl.pathname.startsWith("/dashboard")) \{
return NextResponse.redirect(new URL("/login", request.url));
\}
return NextResponse.next();
\}
export const config = \{
// Coincide con todas las rutas excepto archivos estáticos y API
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
\};
Reglas del middleware:
app/
(shop)/
layout.tsx # Layout de tienda con nav
page.tsx # Inicio
products/
page.tsx # Listado de productos
[slug]/page.tsx # Detalle de producto
cart/page.tsx
checkout/page.tsx
(account)/
layout.tsx # Layout de cuenta
profile/page.tsx
orders/page.tsx
@modal/
default.tsx
(.)products/[slug]/page.tsx # Modal de vista rápida de producto
app/
(auth)/
login/page.tsx
register/page.tsx
(dashboard)/
layout.tsx
page.tsx
@sidebar/
default.tsx
page.tsx
@main/
default.tsx
[section]/page.tsx
settings/
layout.tsx
page.tsx
profile/page.tsx
billing/page.tsx
## Ejemplo en funcionamiento
### Ejemplo 1: El usuario pregunta "¿Cómo creo un patrón de modal para fotos?"
**Prompt del usuario:** "Quiero que al hacer clic en una miniatura de foto se abra un modal, pero al compartir la URL se muestre la página completa de la foto."
**La respuesta guiada por el skill produciría esta estructura de archivos:**
app/ layout.tsx @modal/ default.tsx (.)photo/[id]/page.tsx # Versión modal photo/[id]/page.tsx # Versión de página completa gallery/page.tsx # Cuadrícula de miniaturas
```tsx
// app/layout.tsx
export default function RootLayout({
children,
modal,
}: {
children: React.ReactNode;
modal: React.ReactNode;
}) {
return (
<html lang="en">
<body>
{children}
{modal}
</body>
</html>
);
}
// app/@modal/default.tsx
export default function Default() {
return null; // Sin modal por defecto
}// app/@modal/(.)photo/[id]/page.tsx
import { Modal } from "@/components/Modal";
export default async function PhotoModal({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const photo = await getPhoto(id);
return (
<Modal>
<img src={photo.url} alt={photo.alt} />
<p>{photo.description}</p>
</Modal>
);
}// app/photo/[id]/page.tsx
export default async function PhotoPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const photo = await getPhoto(id);
return (
<main className="flex min-h-screen items-center justify-center">
<img src={photo.url} alt={photo.alt} className="max-w-4xl" />
<h1>{photo.title}</h1>
<p>{photo.description}</p>
</main>
);
}La respuesta guiada por el skill recomendaría:
(marketing) para páginas públicas, (app) para el dashboard autenticado, (auth) para login/registroEste skill proporciona a Claude:
Amplía este skill añadiendo:
mkdir -p .claude/skills/nextjs-routing
# Pega el contenido de Receta en .claude/skills/nextjs-routing/SKILL.md(.) significa el mismo nivel en el sistema de archivos, no en la URL. Esto confunde a muchos desarrolladores.await params en componentes page y layout.| Enfoque | Cuándo usarlo |
|---|---|
| Pages Router | Proyectos legacy de Next.js aún no migrados |
| React Router v7 | Apps React sin Next.js, migración desde Remix |
| TanStack Router | Routing con tipos seguros y validación de search params |
| Expo Router | Routing basado en archivos para React Native + web |
layout.tsx envuelve children y preserva el state entre navegaciones (no se vuelve a montar)template.tsx envuelve children pero se vuelve a montar en cada navegación (state nuevo cada vez)template.tsx cuando necesites animaciones o reinicios de state al cambiar de ruta@folder (p. ej., @modal, @sidebar)layout.tsx padreloading.tsx y error.tsxdefault.tsx para cada slot de ruta paraleladefault.tsx suele devolver null cuando no debe mostrarse contenido@modal) con una ruta interceptada como (.)photo/[id]/page.tsxphoto/[id]/page.tsx(.) significa "interceptar en el mismo nivel del sistema de archivos"(.)folder -- intercepta en el mismo nivel(..)folder -- intercepta un nivel arriba(..)(..)folder -- intercepta dos niveles arriba(...)folder -- intercepta desde la raíz de la app(marketing) o (auth) NO aparecen en la URLapp/(marketing)/about/page.tsx se mapea a /about, no a /marketing/about[...slug] es un catch-all obligatorio -- coincide con /shop/a/b/c pero NO con /shop[[...slug]] es un catch-all opcional -- coincide tanto con /docs como con /docs/a/bexport default async function Page({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
// ...
}params es una Promise y debe hacerse awaitpage.tsx en lugar de route.tsdefault.tsx en rutas paralelasredirect() en Client Components en lugar de middleware(marketing) para páginas públicas, (app) para el dashboard, (auth) para login/registrolayout.tsxexport default function RootLayout({
children,
modal,
}: {
children: React.ReactNode;
modal: React.ReactNode;
}) {
return (
<html lang="en">
<body>{children}{modal}</body>
</html>
);
}@slot se convierte en una prop tipada como React.ReactNodeRevisado por Chris St. John·Última actualización: 7 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥