Mascota Jacar — leyendo contigo Un portátil cuyos ojos siguen el cursor mientras lees.
Desarrollo de Software Experiencia de Usuario

Next.js App Router: lecciones de migrar proyectos reales

Next.js App Router: lecciones de migrar proyectos reales

Actualizado: 2026-05-03

Next.js App Router se declaró estable en la versión 13.4, en mayo de 2023. Un año después, con Next.js 14.2 como línea principal, los equipos que dieron el salto tienen lecciones concretas que contar. Algunas alegres, otras dolorosas, casi todas ausentes de la documentación oficial. Este artículo recoge lo que vemos en proyectos reales: qué compensa, qué cuesta y cómo abordar la transición sin hundir la confianza del equipo en el framework.

Puntos clave

  • App Router es un cambio de modelo mental, no un cambio de sintaxis: los componentes son servidor por defecto.
  • El caching por capas (Request Memoization, Data Cache, Full Route Cache, Router Cache) es la principal fuente de confusión el primer mes.
  • La frontera Server Component / Client Component y la directiva 'use client' deben colocarse lo más abajo posible en el árbol.
  • Los Server Actions eliminan API routes para operaciones simples; para flujos complejos con optimistic updates, tRPC o endpoints explícitos siguen siendo mejores.
  • Para proyectos consolidados en Pages Router: migración incremental ruta a ruta, no big bang. Pages Router seguirá recibiendo mantenimiento indefinidamente.

Un cambio de modelo, no de sintaxis

La tentación inicial es tratar App Router como “Pages Router con otra organización de carpetas”. Es un error caro. El nuevo router cambia el modelo mental de toda la aplicación:

  • Los componentes se ejecutan en el servidor por defecto.
  • El fetching de datos se hace con async/await directamente en el árbol de renderizado.
  • Los layouts se anidan por estructura de directorios y mantienen estado entre navegaciones.
  • El caching pasa de ser algo explícito a ser un sistema con capas que hay que entender antes de depurar.

Las convenciones de fichero (page.tsx, layout.tsx, loading.tsx, error.tsx, not-found.tsx, route.ts) sustituyen a pages/* y getServerSideProps. No son imports opcionales: son hooks del sistema de rutas con contratos específicos.

El fetching natural es probablemente la mejor parte:

typescript
// app/users/[id]/page.tsx
export default async function UserPage({ params }: { params: { id: string } }) {
  const user = await db.user.findUnique({ where: { id: params.id } });
  const posts = fetchPosts(params.id); // sin await: se resuelve dentro de Suspense

  return (
    <section>
      <UserCard user={user} />
      <Suspense fallback={<PostsSkeleton />}>
        <PostsList promise={posts} />
      </Suspense>
    </section>
  );
}

En una página de detalle típica, el bundle enviado al navegador se reduce fácilmente un 30-40% respecto a Pages Router gracias a los Server Components. Eso se traduce en TTI más bajos, sobre todo en móviles de gama media.

Lo que duele de verdad

El caching es el mayor dolor de cabeza del primer mes. Next.js cachea agresivamente por defecto: resultados de fetch, páginas completas, segmentos del router. El resultado típico es que un endpoint que antes devolvía datos frescos ahora devuelve algo de hace horas. Hay cuatro niveles de cache y cada uno tiene su mecanismo de invalidación:

  • cache: 'no-store' en fetch para datos siempre frescos.
  • export const revalidate = 60 para revalidación temporal.
  • export const dynamic = 'force-dynamic' para rutas sin cache.
  • revalidatePath() o revalidateTag() desde Server Actions para invalidación explícita.

Aprender cuál usar cuándo lleva semanas de producción real.

La frontera Server / Client Component es la segunda fuente de fricción. La directiva 'use client' marca un componente y toda su descendencia como cliente; colocarla demasiado arriba en el árbol anula buena parte de las ventajas de Server Components. Las librerías CSS-in-JS (Material-UI, Chakra, Emotion) requieren providers marcados como Client Component. Tailwind, al ser compilado, no sufre el problema.

Los errores de hidratación son los más dolorosos: un Server Component que renderiza una fecha con la timezone del servidor y un Client Component que al hidratar formatea con la del navegador producen un desajuste que React detecta con un mensaje vago. Hay que pensar explícitamente qué datos son estables entre servidor y cliente.

Server Actions

Los Server Actions son mutations escritas como funciones asíncronas marcadas con 'use server' y pasadas al atributo action de un <form>. Eliminan la necesidad de crear API routes para operaciones simples, integran progressive enhancement y se combinan con revalidatePath para invalidar cache tras una escritura.

En proyectos con mutations simples (crear, actualizar, borrar), reducen boilerplate considerablemente. En flujos complejos con validación intrincada, optimistic updates y reintentos, siguen siendo preferibles endpoints explícitos o librerías como tRPC. Encaja bien con frameworks como Remix v2 que también apuestan por mutations basadas en formularios.

Migrar sin detener la máquina

Next.js permite coexistencia: app/ y pages/ conviven en el mismo proyecto. La estrategia realista para cualquier aplicación en producción es incremental:

  1. Mantener las rutas existentes en Pages Router.
  2. Escribir nuevas features en App Router.
  3. Migrar rutas antiguas cuando hay motivo (reescritura de UI, bug que justifica tocar el fichero).

Para un proyecto mediano de 30-60 rutas, esperan entre dos y seis meses de migración real. El patrón que mejor funciona: empezar por rutas de lectura pura (listados, detalles, landings) y dejar para el final las que tengan lógica compleja de cliente (dashboards interactivos, editores, wizards con múltiples pasos).

Un error común es migrar “por obligación” sin plan. Vercel ha confirmado que Pages Router seguirá recibiendo mantenimiento indefinidamente. La deuda técnica de no migrar no existe todavía; la de migrar mal, sí.

Despliegue y rendimiento

En Vercel el despliegue es transparente. Fuera de Vercel hay dos caminos: self-hosting con output: 'standalone' sobre Node.js en contenedor (la opción más predecible), o el adaptador OpenNext para Lambda. Cloudflare Pages todavía no soporta todas las features de App Router: revisar la tabla de compatibilidad antes de comprometerse.

Números medidos en migraciones reales:

  • TTFB mejora típicamente 30-50% gracias al streaming.
  • El bundle de cliente se reduce 30-45% si se usan bien los Server Components.
  • LCP y FCP mejoran visiblemente en páginas con data fetching.
  • Los builds tardan más y el consumo de CPU en servidor sube porque ahora renderiza más.

App Router es un salto significativo pero no gratuito. Para proyectos nuevos, la recomendación es empezar directamente con App Router. Para proyectos consolidados en Pages Router, migrar con cabeza, ruta a ruta, sin prisa y con métricas que justifiquen cada paso.

¿Te ha resultado útil?
[Total: 14 · Media: 4.4]

Escrito por

CEO - Jacar Systems

Apasionado de la tecnología, la infraestructura cloud y la inteligencia artificial. Escribe sobre DevOps, IA, plataformas y software desde Madrid.