Przejdź do treści

App Router czy Pages Router w Next.js: co wybrać?

Porównanie App Router i Pages Router w Next.js 16: rendering, cache, wydajność, routing oraz bezpieczna migracja istniejącego projektu.

Maciej Sala

Founder StriveLab

9 min czytaniaAktualizacja

W 2026 oba routery są nadal wspierane, ale większość nowych możliwości Next.js trafia najpierw do App Router. W tym artykule porównam oba rozwiązania bez marketingowego szumu i pokażę nie tylko zalety, ale też realny koszt migracji, granice ekosystemu i momenty, w których Pages Router wciąż bywa rozsądnym wyborem.

App Router vs Pages Router: szybkie porównanie

KryteriumPages RouterApp Router
Folderpages/app/
Model komponentówstrony hydratowane po stronie klientaServer Components domyślnie, jawne granice klienta
Pobieranie danychgetServerSideProps, getStaticPropsasync Server Components i funkcje danych
Layouty_app.tsx i opcjonalny getLayoutnatywne, zagnieżdżone, zachowujące stan
Loading i błędywłasne komponenty i obsługa routeraloading.tsx, error.tsx, Suspense
Metadatanext/headMetadata API i generateMetadata
JavaScript klientowycały moduł strony podlega hydratacjizależy od granic 'use client'
Streaming trasbrak natywnego modelu segmentówwbudowany z Suspense
Statuswspieranyrekomendowany dla najnowszych funkcji
Krzywa uczenianiższa dla znających klasyczny Reactwyższa przez RSC, cache i granice środowisk

Czym różni się App Router od Pages Router?

Zanim przejdziemy do szczegółów, warto zwrócić uwagę na fundamentalną różnicę:

Pages Router to tradycyjny routing Next.js, w którym pliki w folderze pages/ automatycznie stają się trasami. Strony mogą być prerenderowane przez , lub ISR, ale kod komponentu strony jest wysyłany do przeglądarki i hydratowany. Określenie „Client Component” należy przede wszystkim do modelu RSC używanego w App Routerze.

App Router opiera się na komponentach serwerowych z biblioteką React Server Components i routingu w folderze app/. Domyślnie wszystkie komponenty to Server Components, a layouts, loading states i error boundaries są wbudowane w strukturę folderów.

Code
# Pages Router
pages/
├── index.tsx          → /
├── about.tsx          → /about
├── blog/
│   ├── index.tsx      → /blog
│   └── [slug].tsx     → /blog/:slug
└── _app.tsx           → wrapper dla wszystkich stron

# App Router
app/
├── page.tsx           → /
├── layout.tsx         → wrapper (dziedziczony)
├── about/
│   └── page.tsx       → /about
└── blog/
    ├── page.tsx       → /blog
    └── [slug]/
        └── page.tsx   → /blog/:slug

Kluczowe różnice między App Router i Pages Router

1. Pobieranie danych w App Router i Pages Router

Tutaj zaszła największa zmiana i jest to też źródło największych komplikacji.

Pages Router korzysta z dedykowanych funkcji eksportowanych z komponentu strony:

Code
// pages/blog/[slug].tsx
export async function getStaticProps({ params }) {
  const post = await getPost(params.slug)
  return { props: { post } }
}
 
export async function getStaticPaths() {
  const posts = await getAllPosts()
  return {
    paths: posts.map((post) => ({ params: { slug: post.slug } })),
    fallback: false,
  }
}
 
export default function BlogPost({ post }) {
  return <article>{post.title}</article>
}

App Router pobiera dane bezpośrednio w komponencie:

Code
// app/blog/[slug]/page.tsx
type Props = {
  params: Promise<{ slug: string }>
}
 
export default async function BlogPost({ params }: Props) {
  const { slug } = await params
  const post = await getPost(slug)
  return <article>{post.title}</article>
}
 
export async function generateStaticParams() {
  const posts = await getAllPosts()
  return posts.map((post) => ({ slug: post.slug }))
}

W Next.js 16 params i searchParams stron są Promise. generateStaticParams() dostarcza znane parametry do prerenderowania, ale nie zastępuje decyzji o cache i obsłudze nieznanych ścieżek.

Model cache zależy od konfiguracji projektu. Bez Cache Components wywołania fetch() nie są domyślnie cache'owane; cache włączasz przez cache: 'force-cache' lub rewalidację. Opcjonalne cacheComponents: true w Next.js 16 udostępnia model oparty na 'use cache', cacheLife i cacheTag. Przed migracją ustal jeden model i opisz go w zasadach projektu, żeby zespół świadomie odróżniał dane świeże, cache'owane i unieważniane po mutacji.

2. Layouty w App Router i Pages Router

Pages Router ma jeden globalny _app.tsx i opcjonalnie per-page layouts przez pattern z getLayout:

Code
// pages/_app.tsx
export default function App({ Component, pageProps }) {
  const getLayout = Component.getLayout || ((page) => page)
  return getLayout(<Component {...pageProps} />)
}
 
// pages/dashboard.tsx
Dashboard.getLayout = (page) => <DashboardLayout>{page}</DashboardLayout>

App Router ma layouty natywne, zagnieżdżone i zachowujące stan:

Code
// app/dashboard/layout.tsx
export default function DashboardLayout({ children }) {
  return (
    <div className="flex">
      <Sidebar />
      <main>{children}</main>
    </div>
  )
}
 
// app/dashboard/settings/layout.tsx
export default function SettingsLayout({ children }) {
  return (
    <div>
      <SettingsNav />
      {children}
    </div>
  )
}

Layouty w App Router mogą zachowywać stan i nie być odtwarzane przy każdej nawigacji w obrębie tego samego drzewa. To ogromna różnica dla , ale tylko wtedy, gdy sensownie zaprojektujesz granice layoutów i nie przeniesiesz zbyt wiele logiki do Client Components.

3. Loading i error states w routingu Next.js

Pages Router nie ma segmentowych konwencji loading i error. Dane z getServerSideProps lub getStaticProps są gotowe przed renderowaniem strony, natomiast stan przejścia klientowego trzeba obsłużyć przez zdarzenia routera, bibliotekę danych albo własny komponent. Błędy można przechwytywać przez _error.tsx, error boundary lub obsługę właściwą dla konkretnego źródła danych.

App Router integruje loading i error boundaries ze strukturą tras:

Code
app/dashboard/
├── page.tsx        → główna treść
├── loading.tsx     → pokazywany podczas ładowania
├── error.tsx       → pokazywany przy błędzie
└── not-found.tsx   → pokazywany dla 404
Code
// app/dashboard/loading.tsx
export default function Loading() {
  return <Skeleton />
}
Code
// app/dashboard/error.tsx
'use client'
 
export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    <div>
      <h2>Coś poszło nie tak</h2>
      <p>Id błędu: {error.digest ?? 'brak'}</p>
      <button onClick={reset}>Spróbuj ponownie</button>
    </div>
  )
}

loading.tsx automatycznie tworzy granicę Suspense dla strony i jej potomków oraz zapewnia natychmiastowy fallback podczas nawigacji. error.tsx musi być Client Componentem i przechwytuje błędy zagnieżdżone poniżej granicy, ale nie błąd layoutu z tego samego segmentu. Dla root layoutu służy global-error.tsx.

4. Server Components vs Client Components

W Pages Router kod komponentu strony jest hydratowany w przeglądarce, nawet jeśli HTML powstał przez SSR lub SSG. Import używany podczas renderowania komponentu może więc trafić do klientowego bundla. Kod wykorzystywany wyłącznie w getStaticProps albo getServerSideProps jest usuwany z bundla klienta.

Code
// pages/product.tsx
import { formatPrice } from 'heavy-library' // trafia do bundle'a
 
export default function Product({ product }) {
  return <p>{formatPrice(product.price)}</p>
}

App Router domyślnie używa Server Components, więc kod zostaje na serwerze:

Code
// app/product/[id]/page.tsx
import { formatPrice } from 'heavy-library' // NIE trafia do bundle'a
 
export default async function Product({
  params,
}: {
  params: Promise<{ id: string }>
}) {
  const { id } = await params
  const product = await getProduct(id)
  return <p>{formatPrice(product.price)}</p>
}

Może to zmniejszyć JavaScript wysyłany do przeglądarki, ale nie robi tego automatycznie. Dyrektywa 'use client' wyznacza granicę modułów: importy i komponenty poniżej tej granicy należą do klientowego grafu. Najlepszy efekt daje pozostawienie dużej części strony na serwerze i wydzielenie małych, interaktywnych komponentów.

5. Metadata i SEO w App Router i Pages Router

Pages Router używa komponentu Head lub next/head:

Code
// pages/about.tsx
import Head from 'next/head'
 
export default function About() {
  return (
    <>
      <Head>
        <title>O nas | Moja Strona</title>
        <meta name="description" content="Opis strony" />
        <meta property="og:title" content="O nas" />
      </Head>
      <main>...</main>
    </>
  )
}

App Router używa Metadata API z pełnym typowaniem:

Code
// app/about/page.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'O nas | Moja Strona',
  description: 'Opis strony',
  openGraph: {
    title: 'O nas',
  },
}
 
export default function About() {
  return <main>...</main>
}

Metadata API obsługuje statyczny obiekt metadata, dynamiczną funkcję generateMetadata oraz specjalne pliki dla ikon, Open Graph, sitemap i robots. Eksporty metadanych działają wyłącznie w Server Components. Nie daje to App Routerowi automatycznej przewagi SEO nad poprawnie wyrenderowaną stroną Pages Routera, ale centralizuje konfigurację i typowanie.

Wydajność App Router vs Pages Router

AspektPages RouterApp Router
JavaScript klientowygraf modułów stronygraf poniżej granic 'use client'
hydratacja drzewa stronyhydratacja Client Components i uzgodnienie RSC
Streaming segmentówbrak natywnej konwencjiSuspense i loading.tsx
Trwałe layoutywłasny getLayout i stan aplikacjinatywne layouty zachowujące stan
CacheSSG, ISR, SSR i cache danychcache danych, tras lub Cache Components

App Router daje więcej narzędzi do ograniczania JavaScriptu i progresywnego renderowania, ale wynik zależy od architektury. Duży layout oznaczony 'use client' może zniwelować przewagę RSC, a dobrze zbudowana strona SSG w Pages Routerze może mieć bardzo dobre LCP. Porównuj rozmiar JavaScriptu, TTFB, LCP, INP, liczbę requestów i zachowanie nawigacji, nie samą nazwę routera.

Problemy App Routera, które warto znać przed wyborem

App Router nie zawsze jest idealnym rozwiązaniem dla każdego zespołu i w każdej sytuacji. Przed wyborem uwzględnij:

  • Rozróżnienie kodu serwerowego od klienckiego: trudności w odróżnieniu kodu serwerowego od klienta, czyli która część kodu działa na serwerze, a która w przeglądarce. Jest to szczególnie problematyczne, gdy używasz bibliotek, które potrzebują dostępu do funkcji przeglądarki (np. window) lub specjalnych funkcji Reacta (hooków), które działają tylko po stronie klienta.
  • Dwa modele cache w Next.js 16: zespół musi wiedzieć, czy projekt używa Cache Components, i dobrać do tego fetch, 'use cache', rewalidację oraz invalidację po mutacji.
  • Testowanie komponentów serwerowych: część przypadków można testować integracyjnie, ale złożone przepływy RSC, streaming i granice routera warto pokryć testami E2E. Nie oznacza to rezygnacji z testów jednostkowych dla czystej logiki.
  • Nowe koncepcje: Server Components, serializowalne propsy, streaming, Server Actions i granice środowisk wymagają czasu na naukę oraz przeglądu używanych bibliotek.

Kiedy wybrać Pages Router w projekcie Next.js?

  • utrzymujesz stabilny projekt i nie potrzebujesz funkcji App Routera,
  • koszt regresji przewyższa spodziewaną korzyść migracji,
  • kluczowe biblioteki, niestandardowy _document albo model providerów wymagają najpierw przebudowy,
  • zespół nie ma jeszcze testów chroniących logowanie, płatności i krytyczne formularze,
  • musisz dostarczyć funkcję biznesową, a migrację możesz zaplanować jako osobny projekt techniczny.

„Prosta strona” nie jest sama w sobie argumentem za Pages Routerem. Dla nowego landing page'a App Router również będzie prostym i aktualnym wyborem.

Kiedy wybrać App Router w projekcie Next.js?

  • zaczynasz nowy projekt i chcesz korzystać z aktualnego modelu Next.js,
  • chcesz pobierać dane w Server Components i ograniczyć klientowy graf modułów,
  • potrzebujesz zagnieżdżonych layoutów, streamingu lub progresywnych stanów ładowania,
  • planujesz formularze i mutacje oparte na Server Actions,
  • chcesz używać Metadata API, Route Handlers albo opcjonalnych Cache Components.

SEO nie powinno samodzielnie rozstrzygać wyboru. Oba routery mogą wygenerować indeksowalny HTML, poprawne linki kanoniczne, sitemapę i dane strukturalne. App Router oferuje wygodniejsze API, nie automatycznie lepsze pozycje.

Czy można używać App Router i Pages Router w jednym projekcie?

Foldery pages/ i app/ mogą współistnieć, co pozwala migrować kolejne URL-e oddzielnie.

Code
my-project/
├── app/              # nowe strony w App Router
│   ├── dashboard/
│   └── settings/
├── pages/            # legacy strony
│   ├── old-page.tsx
│   └── api/          # API routes (tylko w pages/)

Ta sama trasa nie może istnieć w obu folderach, ponieważ spowoduje konflikt podczas budowania aplikacji. Jest też ważny koszt hybrydy: przejście między URL-em obsługiwanym przez Pages Router i URL-em App Routera wykonuje pełną nawigację dokumentu. Automatyczny prefetch next/link nie działa pomiędzy routerami, a pamięć klientowa poza trwałym storage zostaje zresetowana.

Podczas migracji nadal utrzymuj _app.tsx i _document.tsx dla starych tras. Root layout z app/ ich nie opakowuje, a globalne style zaimportowane tylko w app/layout.tsx nie obejmą stron z pages/.

Migracja z Pages Router do App Router

Bezpieczna kolejność migracji wygląda następująco:

  1. Zaktualizuj wspólne API, takie jak Image, Link, Script i wersje Reacta, jeszcze przed przenoszeniem tras.
  2. Utwórz root layout i przenieś odpowiednie elementy _app oraz _document, pozostawiając stare pliki dla Pages Routera.
  3. Wydziel providery do Client Componentu, ponieważ React Context nie działa bezpośrednio w Server Componentach.
  4. Przenieś statyczną trasę pilotażową i sprawdź style, metadata, analitykę oraz nawigację do obu routerów.
  5. Zmień hooki routingu: next/router zastąp przez useRouter, usePathname, useSearchParams lub useParams z next/navigation.
  6. Migruj dane świadomie: osobno odwzoruj SSR, SSG, ISR, fallbacki i invalidację cache zamiast mechanicznie usuwać getStaticProps.
  7. Zostaw pages/api albo przenieś wybrane endpointy do Route Handlers. Nie zamieniaj endpointu na Server Component, jeśli korzystają z niego inne klienty.
  8. Uruchom testy E2E i pomiary wydajności przed przeniesieniem kolejnej grupy tras.

Najłatwiejszym pierwszym krokiem dla złożonej strony jest przeniesienie jej dotychczasowego komponentu do pliku z 'use client' i wyrenderowanie go z nowego Server Componentu page.tsx. Dopiero później przesuwaj pobieranie danych i statyczne fragmenty na serwer. Dzięki temu migracja zmienia jedną granicę naraz.

App Router czy Pages Router: moja rekomendacja

Nowy projekt w 2026? Zacznij od App Routera, chyba że konkretna zależność lub wymaganie platformowe wymusza Pages Router. To ścieżka rekomendowana w aktualnej dokumentacji dla najnowszych funkcji React i Next.js.

Istniejący projekt? Najpierw wykonaj audyt. Zostań przy Pages Router, jeśli działa, a migracja nie ma mierzalnego celu. Migruj, gdy uprości layouty i dane, ograniczy JavaScript, ułatwi rozwój albo odblokuje konkretną funkcję. Sam status „nowszego routera” nie uzasadnia ryzyka regresji.

Uczysz się Next.js? Zacznij od App Routera i jego modelu routingu, renderowania oraz pobierania danych. Pages Router nadal warto rozumieć, ponieważ wiele istniejących projektów go używa, ale nowe funkcje poznawaj przede wszystkim w modelu App Router.

Elastyczne i wydajne narzędzia dla biznesu, które dotrzymają kroku Twojemu rozwojowi.
Next.js

Często zadawane pytania

Czy Pages Router zostanie usunięty z Next.js?

Next.js nadal wspiera Pages Router. Dokumentacja rekomenduje App Router, jeśli potrzebujesz najnowszych funkcji React i Next.js, ale działającego projektu nie trzeba przepisywać bez konkretnej korzyści biznesowej lub technicznej.

Co wybrać do nowego projektu w 2026 roku?

Dla większości nowych projektów rozsądniejszym wyborem jest App Router, szczególnie jeśli chcesz korzystać z React Server Components, Server Actions, zagnieżdżonych layoutów i streamingu.

Kiedy nie migrować z Pages Routera?

Nie migruj tylko dlatego, że istnieje nowsza opcja. Migracja ma sens, gdy upraszcza architekturę, ogranicza JavaScript klientowy albo odblokowuje funkcję potrzebną w projekcie. Nie migruj przed pilnym releasem ani bez testów kluczowych ścieżek.

Czy można używać App Router i Pages Router w tym samym projekcie?

Tak, foldery pages i app mogą współistnieć, o ile nie definiują tego samego URL-a. Przejście pomiędzy routerami jest jednak pełną nawigacją, a automatyczny prefetch next/link nie działa między nimi, więc stan klientowy może zostać utracony.

Co to są React Server Components i dlaczego App Router ich używa?

Server Components renderują się w środowisku serwerowym, a ich kod nie trafia do klientowego bundla. Pozwalają pobierać dane blisko źródła i przekazywać wynik do małych Client Components. Mniejszy bundle jest możliwy, ale zależy od właściwego ustawienia granic use client.

Ile zajmuje migracja z Pages Router na App Router?

Nie da się podać wiarygodnego czasu bez audytu. Koszt zależy od liczby tras, getServerSideProps i getStaticProps, providerów w _app, zawartości _document, używanych hooków routera, CSS-in-JS, testów oraz zależności od klientowych bibliotek.

App Router czy Pages Router, co jest szybsze?

App Router ma potencjalnie lepszą wydajność dzięki Server Components (mniejszy bundle JS) i streamingowi HTML z Suspense. Jednak sama zmiana routera nie gwarantuje automatycznej poprawy. Pages Router z dobrym SSG lub ISR może mieć bardzo dobre wyniki. Trzeba porównać JavaScript, TTFB, LCP, cache i zachowanie nawigacji dla konkretnej aplikacji.

Czy biblioteki CSS-in-JS działają z App Router?

Biblioteki generujące style w runtime mogą wymagać Client Componentu i registry do zbierania stylów po stronie serwera. Przed migracją sprawdź instrukcję dla używanej biblioteki i wersji. CSS Modules, globalny CSS i rozwiązania kompilowane w buildzie zwykle integrują się prościej.

O autorze

Maciej Sala

Maciej Sala — konsultant technologiczny produktów cyfrowych i web developer z bogatym doświadczeniem w marketingu internetowym oraz SEO. Na co dzień pracuje z Reactem, Next.js i TypeScriptem, a ostatnio także z Astro i narzędziami do automatyzacji procesów AI. Sprawnie łączy perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat był związany z branżą gier wideo jako project manager i game designer. Absolwent historii na Uniwersytecie Jagiellońskim oraz studiów podyplomowych z marketingu internetowego na AGH w Krakowie. Po godzinach trenuje na siłowni, maluje figurki i rozwija własne projekty.

Pomagam przekładać takie tematy na konkretne wdrożenia w frontendzie, SEO, analityce i procesie produktowym.

Skontaktuj się ze mną
LH.pl – Hosting Mango

Biblioteka wiedzy na temat Next.js

Czytaj dalej

Zobacz więcej wpisów
Pages Router do App Router: Migracja w Next.js

Masz działający projekt na Pages Router i chcesz wejść w Server Components, streaming albo Server Actions. Tylko że przepisanie wszystkiego naraz to proszenie się o regresję, więc tego lepiej nie robić. Rozwiązaniem jest Pages i App Router obok siebie w jednym repozytorium i przenoszenie strona po stronie, potem testy i bezpieczne deploye.

Maciej Sala

Maciej Sala

Founder StriveLab

React Server Components i Server Actions: SEO i wydajność

App Router pozwala przesunąć pobieranie danych i renderowanie treści na serwer, zostawiając w przeglądarce małe obszary interakcji. Taki podział może ograniczyć JavaScript i ułatwić dostarczenie treści w HTML, ale nie gwarantuje dobrych Core Web Vitals ani indeksacji. Wynik zależy też od czasu odpowiedzi serwera, cache, obrazów, fontów, metadanych i bezpieczeństwa mutacji.

Maciej Sala

Maciej Sala

Founder StriveLab

Frontend w Next.js i React: gdzie dziś przebiega granica serwer–klient

Komponent działa lokalnie, ale po wdrożeniu na produkcję pojawia się niezgodność hydratacji ang. hydration mismatch , a prosta strona produktu pobiera kilkaset kilobajtów JavaScriptu. Zamiast natychmiast wyłączać SSR czy otaczać kod memoizacją, warto cofnąć się o krok. Problem rzadko leży w braku optymalizacji — częściej w braku decyzji, która część interfejsu realnie wymaga przeglądarki. React Server Components przesunęły granicę między serwerem a klientem. W App Routerze najpierw decydujemy, gdzie wykonać pracę, a dopiero potem dobieramy hooki i biblioteki.

Maciej Sala

Maciej Sala

Founder StriveLab

LH.pl – Cloud Server 1C4G