𝗛𝗼𝘄 𝗪𝗲 𝗖𝘂𝘁 𝗦𝗹𝗼𝘄 𝗥𝗲𝘀𝗽𝗼𝗻𝘀𝗲𝘀 𝗯𝘆 𝟴𝟬%
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.
𝗧𝗮𝗸𝗲𝗮𝘄𝗮𝘆𝘀
- Use parallel routing to migrate without stopping feature work.
- A custom server layer helps you control traffic and fix framework gaps.
- Streaming requires configuration at every layer. Check Nginx and your CDN.
- Test your SEO metrics during the rollout.
Jak zredukowaliśmy czas wolnych odpowiedzi o 80%, migrując na Next.js App Router
W Subito naszym priorytetem jest dostarczanie szybkich i responsywnych doświadczeń użytkownikom. Jednak wraz ze wzrostem naszej platformy, zaczęliśmy zauważać problem: coraz więcej odpowiedzi serwera było zbyt wolnych, co negatywnie wpływało na nasze wskaźniki wydajności i satysfakcję użytkowników.
Problem: Ograniczenia Pages Routera
Przez długi czas nasza aplikacja opierała się na Pages Routerze w Next.js. Choć był to sprawdzony model, napotkaliśmy na kilka barier, które utrudniały nam osiągnięcie optymalnej wydajności:
1. Duże paczki JavaScript i narzut hydratacji
W Pages Routerze większość logiki pobierania danych i renderowania odbywała się po stronie klienta. Oznaczało to, że przeglądarki użytkowników musiały pobierać duże ilości kodu JavaScript, aby "ożywić" (hydratacja) interaktywne elementy strony. Ten proces nie tylko obciążał procesory urządzeń mobilnych, ale także opóźniał moment, w którym użytkownik mógł zacząć wchodzić w interakcję z aplikacją.
2. Kaskadowe pobieranie danych (Waterfalls)
Pobieranie danych w Pages Routerze często prowadziło do tzw. "waterfalli". Najpierw musieliśmy pobrać dane na poziomie strony (np. przez getServerSideProps), a dopiero po ich otrzymaniu komponenty potomne mogły rozpocząć własne zapytania. To tworzyło sekwencyjną strukturę zapytań, która drastycznie wydłużała czas oczekiwania na pełne wyrenderowanie strony.
Rozwiązanie: Migracja na App Router
Decyzja o migracji na App Router była kluczowa. Nowy model wprowadzony przez Next.js pozwolił nam na pełne wykorzystanie potencjału React Server Components (RSC).
React Server Components (RSC)
Dzięki RSC możemy teraz wykonywać logikę pobierania danych bezpośrednio na serwerze. Największą zaletą jest to, że kod potrzebny do pobrania danych nie jest wysyłany do klienta. Zamiast przesyłać ogromne paczki JavaScript, serwer wysyła gotowy wynik w formie lekkiego formatu, co znacząco redukuje rozmiar bundle'a i przyspiesza czas do pierwszego bajtu (TTFB).
Streaming i Suspense
Kolejnym krokiem było wdrożenie streamingu przy użyciu komponentu Suspense. Zamiast czekać, aż wszystkie dane na stronie zostaną pobrane, co blokowało renderowanie całej strony, możemy teraz wysyłać ją fragmentami.
Dzięki temu użytkownik może natychmiast zobaczyć szkielet strony (skeleton UI) i najważniejsze elementy, podczas gdy wolniejsze komponenty (np. listy produktów czy panele boczne) ładują się w tle. Gdy dane są gotowe, są one płynnie "wstrzykiwane" do widoku.
Wyniki
Migracja na App Router przyniosła spektakularne rezultaty. Po optymalizacji i pełnym wdrożeniu nowych mechanizmów, odnotowaliśmy:
- 80% redukcji wolnych odpowiedzi: Czas odpowiedzi dla najbardziej obciążonych ścieżek spadł o 80%.
- Znaczne zmniejszenie rozmiaru paczek JS: Dzięki RSC wysyłamy znacznie mniej kodu do przeglądarek.
- Poprawa wskaźników Core Web Vitals: Nasze wskaźniki LCP (Largest Contentful Paint) oraz INP (Interaction to Next Paint) uległy wyraźnej poprawie.
Podsumowanie
Przejście z Pages Routera na App Router było dużym przedsięwzięciem inżynieryjnym, ale korzyści w postaci wydajności i lepszego doświadczenia użytkownika są nie do przecenienia. Wykorzystanie Server Components oraz streamingu pozwoliło nam przełamać bariery, które wcześniej ograniczały naszą aplikację.