Introducido en Next.js 13, el App Router adopta un enfoque fundamentalmente diferente al enrutamiento y renderizado en comparación con el Pages Router tradicional. Ahora estable y ampliamente adoptado, analicemos los conceptos básicos para aquellos que planean migrar.
App Router vs Pages Router
| Característica | Pages Router | App Router |
|---|---|---|
| Enrutamiento | Bajo pages/ | Bajo app/ |
| Componentes | Client Component por defecto | Server Component por defecto |
| Layouts | Implementación manual | layout.js anidable |
| Carga | Implementación manual | Automática mediante loading.js |
| Obtención de datos | getServerSideProps, etc. | async directo dentro de componentes |
1. Enrutamiento basado en archivos
La estructura de carpetas dentro de app/ se asigna directamente a las rutas URL.
app/
├── page.js → /
├── layout.js → Layout compartido para todas las páginas
├── about/
│ └── page.js → /about
└── blog/
├── page.js → /blog
└── [slug]/
└── page.js → /blog/hello-world (ruta dinámica)
// app/blog/[slug]/page.js
export default async function BlogPost({ params }) {
const { slug } = await params;
const post = await getPost(slug);
return <article>{post.title}</article>;
}
2. Sistema de layouts
layout.js se aplica automáticamente a todas las páginas bajo su carpeta. Los layouts se pueden anidar.
// app/layout.js (layout raíz)
export default function RootLayout({ children }) {
return (
<html lang="es">
<body>
<header>Encabezado del sitio</header>
{children}
<footer>Pie del sitio</footer>
</body>
</html>
);
}
// app/blog/layout.js (layout de la sección blog)
export default function BlogLayout({ children }) {
return (
<div className="blog-wrapper">
<aside>Barra lateral</aside>
<main>{children}</main>
</div>
);
}
3. Estados de carga y error
Coloca loading.js y error.js en una carpeta para obtener automáticamente interfaces de carga y error.
// app/blog/loading.js
export default function Loading() {
return <div>Cargando...</div>;
}
// app/blog/error.js
'use client';
export default function Error({ error, reset }) {
return (
<div>
<h2>Algo salió mal</h2>
<button onClick={reset}>Intentar de nuevo</button>
</div>
);
}
4. Server Components vs Client Components
En App Router, los Server Components son el valor predeterminado — este es el cambio clave.
| Server Component | Client Component | |
|---|---|---|
| Renderiza en | Servidor | Navegador |
| useState / useEffect | ❌ No disponible | ✅ Disponible |
| Acceso directo a BD | ✅ Posible | ❌ No posible |
| Tamaño del bundle | Pequeño (sin JS enviado) | Grande |
| Interacciones | ❌ No posible | ✅ Posible |
// Server Component (por defecto) — puede acceder a BD directamente
export default async function PostList() {
const posts = await db.query('SELECT * FROM posts');
return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
}
Regla general: escribe Server Components por defecto, y usa Client Components solo para las partes interactivas más pequeñas.
5. Patrones de obtención de datos
App Router te permite llamar a fetch() directamente dentro de los componentes. El almacenamiento en caché está activado por defecto.
// En caché (predeterminado)
const data = await fetch('https://api.example.com/posts');
// Sin caché (nuevo en cada solicitud)
const data = await fetch('https://api.example.com/posts', {
cache: 'no-store',
});
// Revalidación basada en tiempo
const data = await fetch('https://api.example.com/posts', {
next: { revalidate: 60 },
});
6. Grupos de rutas
Envuelve un nombre de carpeta en ( ) para organizar archivos sin afectar la URL.
app/
├── (marketing)/
│ ├── page.js → /
│ └── pricing/
│ └── page.js → /pricing
└── (shop)/
├── layout.js → Layout específico de la tienda
└── products/
└── page.js → /products
Ideal para usar diferentes layouts entre secciones sin prefijos de URL.
Resumen
App Router es una nueva arquitectura centrada en Server Components, y su filosofía difiere significativamente de Pages Router. Puede ser confuso al principio, pero ceñirse a “Server Components primero, Client Components solo donde sea necesario” conducirá a aplicaciones de alto rendimiento. Para migraciones, comienza con páginas más pequeñas y avanza gradualmente.

