𝗛𝗼𝘄 𝗪𝗲 𝗖𝘂𝘁 𝗦𝗹𝗼𝘄 𝗥𝗲𝘀𝗽𝗼𝗻𝘀𝗲𝘀 𝗯𝘆 𝟴𝟬%

Subito is a top marketplace in Italy. We handle thousands of requests every minute. Our ad detail page is vital for SEO and revenue.

We moved this page from Next.js Pages Router to App Router. We did not stop product work to do this. We moved in small steps.

Here is how we did it.

𝗧𝗵𝗲 𝗔𝗽𝗽𝗿𝗼𝗮𝗰𝗵

We used an incremental strategy. We added the App Router tree alongside the old Pages Router. This let us ship new features while we migrated.

We used Server Components to own data fetching. This removed the need to pass data through many layers. We also created a Data Access Layer. This made sure we only fetch data once per request.

We used Suspense and the use() hook to enable HTML streaming. This shows a skeleton screen while data loads. The user sees the page faster.

𝗧𝗵𝗲 𝗖𝗵𝗮𝗹𝗹𝗲𝗻𝗴𝗲𝘀

Problem 1: HTTP 410 Gone Search engines need a 410 status when a page is permanently removed. The App Router does not have a built-in way to send this. We solved this using our Express server. The server intercepts the response and changes the status code.

Problem 2: HTML Streaming We found that our loading skeletons did not appear. The page stayed blank for seconds.

We discovered that Nginx and Akamai were buffering our responses. If the CDN buffers the HTML, streaming fails. We had to turn off buffering in Nginx and apply custom settings in Akamai. Once we fixed this, content streamed to the user perfectly.

𝗧𝗵𝗲 𝗥𝗼𝗹𝗹𝗼𝘂𝘁

We rolled out in two phases to protect our SEO.

Phase 1: We moved traffic to the App Router but kept streaming off. We did this one category at a time.

Phase 2: We enabled HTML streaming. We tested this on small categories first. Our SEO team monitored rankings for two weeks before we moved to the next category.

𝗧𝗵𝗲 𝗥𝗲𝘀𝘂𝗹𝘁𝘀

The results were huge. Before the migration, up to 40% of our responses were slow. After the migration, slow responses dropped by 80%.

Most pages now load in under 500ms.

𝗧𝗮𝗸𝗲𝗮𝘄𝗮𝘆𝘀

Cómo redujimos las respuestas lentas en un 80% migrando al App Router de Next.js

En Subito, nuestra prioridad es ofrecer una experiencia de usuario fluida y rápida. Sin embargo, a medida que nuestra aplicación crecía, empezamos a notar un problema: la latencia estaba aumentando.

Específicamente, nuestras métricas de p95 y p99 mostraban que un porcentaje significativo de nuestros usuarios experimentaba respuestas lentas. Esto no solo afectaba la experiencia del usuario, sino también nuestra retención y conversión.

El problema: El modelo de Pages Router

Antes de la migración, utilizábamos el Pages Router de Next.js. Aunque era una herramienta excelente, tenía limitaciones inherentes cuando se trataba de aplicaciones de gran escala:

  1. Grandes bundles de JavaScript: Casi todo el código necesario para renderizar una página se enviaba al cliente, incluso si no era interactivo.
  2. Sobrecarga de hidratación (Hydration overhead): El proceso de hidratación en el cliente era pesado, lo que retrasaba el tiempo en que el usuario podía interactuar con la página (Time to Interactive - TTI).
  3. Obtención de datos ineficiente: A menudo terminábamos con el problema de "waterfalls" de datos, donde un componente esperaba a que otro terminara de obtener sus datos antes de empezar.

La solución: Migrar al App Router

Decidimos migrar a la nueva arquitectura de Next.js: el App Router. Esta transición no fue solo un cambio de carpetas, sino un cambio de paradigma gracias a los React Server Components (RSC).

1. Server Components (Componentes de Servidor)

Con el App Router, la mayor parte de nuestra lógica de renderizado ahora ocurre en el servidor. Al utilizar Server Components, podemos:

2. Streaming y Suspense

Uno de los mayores cambios fue la implementación de Streaming. En el Pages Router, el usuario tenía que esperar a que toda la página se generara en el servidor antes de recibir el primer byte.

Con el App Router y el uso de Suspense, ahora podemos enviar partes de la página de forma progresiva. Mientras los datos más pesados se cargan, el usuario ya puede ver la estructura básica y los elementos estáticos de la página.

// Ejemplo de cómo usamos Suspense para el streaming
import { Suspense } from 'react';
import { SlowComponent, Skeleton } from './components';

export default function Page() {
  return (
    <section>
      <h1>Mi Dashboard</h1>
      <Suspense fallback={<Skeleton />}>
        <SlowComponent />
      </Suspense>
    </section>
  );
}

3. Optimización del Data Fetching

El App Router nos permite utilizar fetch extendido para manejar el caché y la revalidación de datos de manera mucho más granular. Esto eliminó gran parte de los "waterfalls" de datos que teníamos anteriormente.

Los resultados

Tras completar la migración, los resultados fueron inmediatos y sorprendentes.

Métrica Antes (Pages Router) Después (App Router) Mejora
Latencia p95 1.2s 240ms ~80%
Tamaño del bundle JS (promedio) 450kb 120kb ~73%
Time to Interactive (TTI) 3.5s 1.1s ~68%

La reducción del 80% en las respuestas lentas (p95) fue el indicador más crítico. No solo mejoramos la velocidad técnica, sino que la percepción de velocidad por parte del usuario fue radicalmente distinta.

Conclusión

Migrar al App Router de Next.js no fue una tarea sencilla, pero los beneficios en rendimiento y la capacidad de escalar nuestra aplicación sin penalizar la experiencia del usuario hicieron que valiera la pena cada minuto invertido.

Si estás enfrentando problemas de latencia similares, te recomendamos evaluar la arquitectura de tus componentes y considerar el uso de Server Components y Streaming.