Przepisaliśmy duży e-commerce na Next.js 15 z App Router. Oto szczera relacja - co działa świetnie, co boli, i czego żałujemy.
Sześć miesięcy temu podjęliśmy decyzję o przepisaniu frontendu dużego sklepu internetowego (120k produktów, 50k użytkowników dziennie) z Next.js 12 Pages Router na Next.js 15 z App Router i Server Components. Oto brutalna szczerość o tym, jak poszło.
Dlaczego w ogóle przepisywaliśmy?
Pages Router działał, ale lista problemów rosła:
- Bundle size frontendu przekroczył 400KB gzipped
- Hydration errors przy dynamicznych komponentach
- SEO wymagało coraz więcej workaroundów
- Zespół narzekał na DX - zbyt dużo boilerplate'u
Server Components obiecywały rozwiązanie większości z tych problemów. Kupiliśmy wizję.
Co poszło dobrze
1. Performance - różnica jest realna
Strony produktowe ładują się teraz błyskawicznie. Server Components renderują się na serwerze i streamują do przeglądarki - użytkownik widzi content niemal natychmiast.
2. Data fetching stał się prosty
Koniec z useEffect + useState + loading states dla każdego fetcha. Async/await bezpośrednio w komponencie to game changer:
async function ProductPage({ id }) {
const product = await getProduct(id); // po prostu await
return <ProductDetails product={product} />;
}
3. SEO out of the box
Metadata API w App Router jest genialne. Dynamiczne meta tagi, Open Graph, Twitter Cards - wszystko działa bez dodatkowych bibliotek.
Co bolało (i nadal boli)
1. Krzywa uczenia się zespołu
Myślenie w Server vs Client Components wymaga przemodelowania nawyków. Przez pierwszy miesiąc połowa PR-ów wracała z komentarzami "to powinien być Server Component" lub "potrzebujesz 'use client' tutaj".
Typowe błędy na starcie
- Używanie useState w Server Components
- Przekazywanie funkcji jako props do Server Components
- Zapominanie o 'use client' przy onClick
- Mieszanie async z hooks w jednym komponencie
Rozwiązania
- Jasna konwencja nazewnictwa (*.client.tsx)
- ESLint rules dla Server Components
- Code review checklist
- Wewnętrzna dokumentacja z przykładami
2. Cache invalidation to piekło
Next.js agresywnie cache'uje. Zbyt agresywnie. Spędziliśmy dwa tygodnie na debugowaniu przypadku, gdzie aktualizacja produktu nie pokazywała się przez godzinę mimo revalidatePath().
Końcowe rozwiązanie: własny wrapper na fetch z explicit cache tags i webhook do invalidacji po zmianach w CMS.
3. Biblioteki third-party
Połowa naszych ulubionych bibliotek nie była gotowa na Server Components. Formik? Client only. React Query? Wymaga provider w client component. Framer Motion? Częściowe wsparcie.
Czego żałujemy
Nie zrobiliśmy migracji etapowo. Przepisaliśmy wszystko naraz zamiast route-by-route. To wydłużyło projekt o 2 miesiące i spowodowało chaos w testowaniu.
Gdybym mógł cofnąć czas: najpierw landing page, potem listing, potem PDP, potem checkout. Każdy etap z pełnym QA przed następnym.
Czy było warto?
Tak. Mimo bólu - tak. Performance gains są realne i przełożyły się na lepsze wyniki biznesowe. Zespół po przejściu krzywej uczenia się jest bardziej produktywny. Kod jest czystszy.
Ale: jeśli masz działający Pages Router i nie masz problemów z performance - nie przepisuj na siłę. App Router to przyszłość Next.js, ale migracja to poważne przedsięwzięcie.
Planujesz migrację do Next.js 15?
Przeszliśmy przez to. Możemy pomóc zaplanować migrację etapową i uniknąć naszych błędów.