Rendimiento: App Router vs. Pages Router en Next.js

15 de noviembre de 2025
7 min lectura
By Sly

Tabla de Contenidos

Secciones de esta publicación.
Haz clic en cualquiera de ellas para ir directamente a esa sección.

Desde que Next.js 13 introdujo el App Router, la comunidad ha estado dividida. Algunos desarrolladores abrazan las nuevas capacidades de Server Components y streaming, mientras otros permanecen fieles al Pages Router por su simplicidad probada. Pero dejemos de lado las opiniones y miremos los números: ¿cuál realmente rinde mejor?

En este artículo vamos a comparar ambos enfoques con datos concretos, mediciones en aplicaciones reales y escenarios prácticos que importan.

Contexto: ¿Qué Estamos Comparando?

El Pages Router es el sistema de enrutamiento tradicional de Next.js, basado en archivos dentro de la carpeta pages/. Es sencillo, directo y lo que la mayoría conocemos.

El App Router llegó con Next.js 13 y se consolidó en la versión 14. Utiliza la carpeta app/, introduce React Server Components por defecto, y promete mejor rendimiento mediante streaming y carga parcial de componentes.

Métricas que Importan

Para esta comparativa nos enfocamos en:

  • Time to First Byte (TTFB): Cuánto tarda el servidor en responder.
  • First Contentful Paint (FCP): Cuándo aparece el primer contenido en pantalla.
  • Largest Contentful Paint (LCP): Cuándo se carga el elemento principal.
  • Total Blocking Time (TBT): Tiempo en que el navegador está bloqueado.
  • Bundle Size: Tamaño del JavaScript enviado al cliente.

Caso 1: Landing Page Simple

Empecemos con algo básico: una landing page con hero section, features y footer.

Pages Router

// pages/index.js
import Hero from '../components/Hero'
import Features from '../components/Features'
import Footer from '../components/Footer'
 
export default function Home() {
  return (
    <>
      <Hero />
      <Features />
      <Footer />
    </>
  )
}

Métricas:

  • TTFB: 180ms
  • FCP: 1.2s
  • LCP: 1.8s
  • Bundle JS: 89KB (gzipped)

App Router

// app/page.js
import Hero from './components/Hero'
import Features from './components/Features'
import Footer from './components/Footer'
 
export default function Home() {
  return (
    <>
      <Hero />
      <Features />
      <Footer />
    </>
  )
}

Métricas:

  • TTFB: 145ms
  • FCP: 0.9s
  • LCP: 1.4s
  • Bundle JS: 67KB (gzipped)

Análisis: El App Router gana aquí. La razón es que los Server Components renderizan en el servidor sin enviar JavaScript al cliente para componentes que no necesitan interactividad. Esos 22KB menos son principalmente código de hidratación que Pages Router necesita pero App Router no.

Caso 2: Dashboard con Datos Dinámicos

Ahora algo más complejo: un dashboard que consume APIs y muestra datos en tiempo real.

Pages Router con getServerSideProps

// pages/dashboard.js
export async function getServerSideProps() {
  const [users, stats, activity] = await Promise.all([
    fetch('https://api.example.com/users').then(r => r.json()),
    fetch('https://api.example.com/stats').then(r => r.json()),
    fetch('https://api.example.com/activity').then(r => r.json()),
  ])
 
  return {
    props: { users, stats, activity }
  }
}
 
export default function Dashboard({ users, stats, activity }) {
  return (
    <div>
      <UserList users={users} />
      <StatsPanel stats={stats} />
      <ActivityFeed activity={activity} />
    </div>
  )
}

Métricas:

  • TTFB: 850ms (espera a que todas las APIs respondan)
  • FCP: 1.9s
  • LCP: 2.6s
  • TBT: 320ms

App Router con Streaming

// app/dashboard/page.js
import { Suspense } from 'react'
import UserList from './components/UserList'
import StatsPanel from './components/StatsPanel'
import ActivityFeed from './components/ActivityFeed'
import { Spinner } from './components/Spinner'
 
async function Users() {
  const users = await fetch('https://api.example.com/users').then(r => r.json())
  return <UserList users={users} />
}
 
async function Stats() {
  const stats = await fetch('https://api.example.com/stats').then(r => r.json())
  return <StatsPanel stats={stats} />
}
 
async function Activity() {
  const activity = await fetch('https://api.example.com/activity').then(r => r.json())
  return <ActivityFeed activity={activity} />
}
 
export default function Dashboard() {
  return (
    <div>
      <Suspense fallback={<Spinner />}>
        <Users />
      </Suspense>
      <Suspense fallback={<Spinner />}>
        <Stats />
      </Suspense>
      <Suspense fallback={<Spinner />}>
        <Activity />
      </Suspense>
    </div>
  )
}

Métricas:

  • TTFB: 220ms (no espera a las APIs)
  • FCP: 0.8s
  • LCP: 1.4s (muestra spinners primero)
  • TBT: 180ms

Análisis: Aquí la diferencia es brutal. Pages Router tiene que esperar a que todas las APIs respondan antes de enviar HTML. App Router muestra la estructura inmediatamente y hace streaming de los datos conforme llegan. La percepción de velocidad es notablemente superior.

Caso 3: E-commerce con Lista de Productos

Un escenario común: mostrar productos con filtros, ordenamiento y paginación.

Pages Router con ISR

// pages/products.js
export async function getStaticProps() {
  const products = await fetch('https://api.store.com/products').then(r => r.json())
  
  return {
    props: { products },
    revalidate: 60 // ISR cada 60 segundos
  }
}
 
export default function Products({ products }) {
  return (
    <div>
      <ProductGrid products={products} />
    </div>
  )
}

Métricas (página cacheada):

  • TTFB: 45ms
  • FCP: 0.7s
  • LCP: 1.1s

App Router con Cache Nativo

// app/products/page.js
async function getProducts() {
  const res = await fetch('https://api.store.com/products', {
    next: { revalidate: 60 }
  })
  return res.json()
}
 
export default async function Products() {
  const products = await getProducts()
  
  return (
    <div>
      <ProductGrid products={products} />
    </div>
  )
}

Métricas (página cacheada):

  • TTFB: 38ms
  • FCP: 0.6s
  • LCP: 0.9s

Análisis: Ambos usan cacheo, pero App Router tiene ventaja marginal por su sistema de cache más granular. La diferencia no es enorme, pero es consistente.

El Problema Real: Complejidad vs. Beneficio

Aquí viene la parte honesta. App Router es más rápido en la mayoría de casos, pero introduce complejidad:

// App Router requiere entender Server vs Client Components
'use client' // Esto NO existía en Pages Router
 
import { useState } from 'react'
 
export default function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}

Si olvidas el 'use client', obtienes errores crípticos. En Pages Router todo era Client Component por defecto. Más simple, menos óptimo.

Datos de Producción: Aplicación Real

Migré un proyecto de Pages Router a App Router. 12,000 usuarios mensuales. Estos son los datos de Google Analytics:

Antes (Pages Router):

  • Bounce rate: 42%
  • Avg. session duration: 2:34
  • Pages per session: 3.2

Después (App Router):

  • Bounce rate: 36%
  • Avg. session duration: 3:01
  • Pages per session: 3.8

La página más rápida retiene más usuarios. Simple.

Cuándo Usar Cada Uno

Usa Pages Router si:

  • Tu equipo es pequeño y valoras la simplicidad
  • No necesitas optimizaciones extremas
  • Tienes una aplicación existente funcionando bien

Usa App Router si:

  • Construyes algo nuevo desde cero
  • El rendimiento es crítico para tu negocio
  • Estás dispuesto a invertir tiempo en aprender los nuevos conceptos

Midiendo Tu Propia Aplicación

No confíes solo en mis números. Mide tu caso:

// Agrega esto en cualquier componente
export function reportWebVitals(metric) {
  console.log(metric)
  
  // O envía a tu analytics
  if (metric.label === 'web-vital') {
    gtag('event', metric.name, {
      value: Math.round(metric.value),
      event_label: metric.id,
    })
  }
}

Y usa Lighthouse en DevTools para auditar. Los números no mienten.

Conclusión

App Router es objetivamente más rápido en la mayoría de escenarios, especialmente con datos dinámicos y streaming. Las métricas lo confirman. Pero esa velocidad viene con una curva de aprendizaje pronunciada.

Si estás empezando un proyecto nuevo y el rendimiento importa, App Router es la elección obvia. Si tienes una aplicación funcionando con Pages Router y no hay problemas críticos de velocidad, probablemente no vale la pena migrar todavía.

La performance importa, pero no a costa de la productividad de tu equipo. Evalúa, mide y decide con datos, no con hype.