App Router de Next.js: qué cambia frente a Pages Router

A sleek home technology setup featuring a router, glass decoration, and television.
Foto de Jaycee300s en Pexels

Desde la versión 13, Next.js introdujo el App Router, una forma diferente de organizar rutas y componentes frente al tradicional Pages Router. Aunque ambos siguen sirviendo la misma finalidad –mapear URLs a componentes–, cambian varios conceptos que afectan al rendimiento, a la experiencia de desarrollo y a la forma de manejar datos.

Pages Router: el modelo tradicional

El Pages Router se basa en la carpeta pages/. Cada archivo .js, .tsx o .mdx dentro de ella representa una ruta. Por ejemplo, pages/about.tsx se sirve en /about. Las rutas dinámicas usan corchetes ([id].tsx) y los archivos _app.tsx y _document.tsx permiten envolver la aplicación y personalizar el HTML.

App Router: la nueva forma de trabajar

El App Router introduce la carpeta app/. Cada subcarpeta representa una ruta y dentro de ella se pueden definir varios archivos especiales:

  • page.tsx: el componente que se renderiza para esa ruta.
  • layout.tsx: define una capa de diseño reutilizable para rutas anidadas.
  • loading.tsx: UI que se muestra mientras se cargan datos.
  • error.tsx: manejo de errores a nivel de ruta.

Esta arquitectura permite anidar layouts de forma natural, algo que en Pages Router requería lógica manual.

Estructura de carpetas comparada

// pages/about.tsx
export default function About() {
  return <h1>About page (Pages Router)</h1>;
}

// app/about/page.tsx
export default function AboutPage() {
  return <h1>About page (App Router)</h1>;
}

En el ejemplo anterior, la ruta /about se define con un único archivo en pages/, mientras que en app/ se necesita la subcarpeta about/ y el archivo page.tsx. Aunque parece más verboso, este patrón abre la puerta a layouts anidados y a una gestión más clara de la UI por niveles.

Rutas dinámicas y anidadas

Con Pages Router, una ruta dinámica se escribe pages/posts/[slug].tsx. En App Router, la misma ruta se declara como app/posts/[slug]/page.tsx. La diferencia principal es que ahora puedes colocar un layout.tsx dentro de app/posts/ y otro distinto dentro de app/posts/[slug]/, creando una jerarquía visual sin código extra.

// app/posts/layout.tsx
export default function PostsLayout({children}) {
  return (
    <section>
      <nav>/ menú de posts /</nav>
      <main>{children}</main>
    </section>
  );
}

// app/posts/[slug]/page.tsx
export default function Post({params}) {
  return <article>Contenido del post {params.slug}</article>;
}

El layout se renderiza una sola vez mientras el page cambia, lo que reduce renderizados innecesarios y mejora la experiencia de usuario.

Server Components por defecto

Una de las mayores diferencias es que los componentes dentro de app/ son, por defecto, Server Components. Eso significa que se ejecutan en el servidor y pueden acceder directamente a bases de datos o a APIs sin exponer credenciales al cliente. Si necesitas que un componente sea interactivo, lo declaras como 'use client' al inicio del archivo.

// app/dashboard/page.tsx
export default function Dashboard() {
  // Este componente se ejecuta en el servidor
  const data = await fetch('https://api.example.com/stats').then(r => r.json());

  return (
    <section>
      <h2>Estadísticas</h2>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </section>
  );
}

En Pages Router, cada página es un Client Component a menos que uses getServerSideProps o getStaticProps. Con App Router, la distinción está más clara y el código tiende a ser más limpio.

Transición y carga de datos

El nuevo router introduce fetch nativo dentro de los componentes del servidor, lo que permite carga de datos automática y caching inteligente. Además, la carpeta loading.tsx permite mostrar un spinner o skeleton mientras la petición se resuelve, sin necesidad de gestionar estados de carga manualmente.

// app/profile/loading.tsx
export default function Loading() {
  return <div>Cargando perfil…</div>;
}

Cuando la página app/profile/page.tsx ejecuta un await fetch(...), Next.js muestra automáticamente este componente hasta que la promesa se cumpla.

Ventajas y consideraciones

  • Layouts anidados: simplifican UI compartida.
  • Server Components por defecto: mejor performance y seguridad.
  • Data fetching simplificado: menos boilerplate.
  • Curva de aprendizaje: el nuevo sistema implica cambiar la mentalidad de carpetas y de componentes.
  • Compatibilidad: el Pages Router sigue funcionando; puedes migrar gradualmente.

Conclusión

El App Router no es solo una re‑estructura de carpetas, es una evolución del modelo de renderizado que Next.js propone. Si buscas una arquitectura más modular, con layouts anidados y un manejo de datos más declarativo, vale la pena probar app/. Si tu proyecto ya está consolidado con pages/ y no necesitas esas características, puedes seguir con el Pages Router sin problemas. La buena noticia es que ambos coexistirán, lo que permite migrar paso a paso según tus prioridades.