App Router czy Pages Router — co wybrać?

App Router czy Pages Router w Next.js 16? Konkretne różnice, koszty migracji i praktyczne kryteria wyboru dla nowych oraz istniejących projektów.

Opublikowano

23 grudnia 2025 09:35

Czytanie

8 min czytania

Aktualizacja

15 kwietnia 2026 11:52

Next.js 13 wprowadził App Router, czyli nowy sposób budowania aplikacji oparty na React Server Components. Ale Pages Router nigdzie nie zniknął. Dwa routery, dwa podejścia, jedna decyzja do podjęcia na starcie projektu.

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.

Krótka odpowiedź: Nowy projekt w 2026 — wybierz App Router. Istniejący projekt działający na Pages Router — nie migruj bez konkretnego powodu. Pages Router pozostaje w pełni wspierany i nie zostanie usunięty.

Szybkie porównanie

KryteriumPages RouterApp Router
Folderpages/app/
Domyślny typ komponentuClient ComponentServer Component
Pobieranie danychgetServerSideProps, getStaticPropsasync component, fetch()
Layouty_app.tsx + getLayout patternNatywne, zagnieżdżone, zachowują stan
Loading/Error statesRęczna implementacjaPlik loading.tsx / error.tsx
Metadata / SEO, czyli Search Engine Optimization, to optymalizacja strony pod widoczność w wynikach wyszukiwania.next/headMetadata API, czyli Application Programming Interface, definiuje sposób komunikacji między aplikacjami lub modułami. z TypeScript
Bundle to paczka JavaScriptu lub innych zasobów wysyłana do przeglądarki jako część aplikacji. sizeWiększy (cały kod do klienta)Mniejszy (Server Components)
StreamingBrakWbudowany z Suspense
Status wsparciaUtrzymywany, bez nowych funkcjiAktywny rozwój, nowe funkcje
Krzywa uczenia sięNiska dla znających ReactWyższa (RSC, Server Actions, cache)
Kiedy wybraćIstniejący projekt, proste strony, stabilnośćNowe projekty, dashboard, SSaaS, SEO-heavy

Czym się różnią?

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. Komponenty są Client Components z opcjonalnym SSR, czyli Server-Side Rendering, oznacza generowanie HTML na serwerze przy każdym żądaniu./SSG, czyli Static Site Generation, oznacza generowanie HTML podczas buildu i serwowanie go jako statycznego pliku. przez getServerSideProps i getStaticProps.

App Router — kluczowe w nim są komponenty serwerowe z biblioteką React Server Components wy routing w folderze app/, omyś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

1. Pobieranie danych

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

Pages Router — dedykowane funkcje eksportowane 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 — dane pobierasz bezpośrednio w komponencie:

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

Model pobierania danych w App Router bywa prostszy na poziomie samego kodu, ale dochodzi nowa warstwa decyzji o cache, rewalidacji i granicach między kodem serwerowym, a klienckim. Jeśli interesuje Cię ten temat, możesz zobaczyć więcej o strategiach pobierania danych w artykule o fetch, cache i rewalidacji w Next.js.

2. Layouty

Pages Router — jeden globalny _app.tsx, 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 — layouty są natywne, zagnieżdżone i zachowują 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 UX, czyli User Experience, opisuje całe doświadczenie użytkownika podczas korzystania z produktu., ale tylko wtedy, gdy sensownie zaprojektujesz granice layoutów i nie przeniesiesz zbyt wiele logiki do Client Components.

3. Loading i Error States

Pages Router — musisz sam zaimplementować loading states, ponieważ error boundaries to dodatkowa konfiguracja:

Code
// pages/dashboard.tsx
import { useState, useEffect } from 'react'
 
export default function Dashboard() {
  const [data, setData] = useState(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState(null)
 
  useEffect(() => {
    fetchData()
      .then(setData)
      .catch(setError)
      .finally(() => setLoading(false))
  }, [])
 
  if (loading) return <Skeleton />
  if (error) return <ErrorMessage />
  return <DashboardContent data={data} />
}

App Router — konwencja plików obsługuje to automatycznie:

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 />
}
 
// app/dashboard/error.tsx
;('use client')
 
export default function Error({ error, reset }) {
  return (
    <div>
      <h2>Coś poszło nie tak</h2>
      <button onClick={reset}>Spróbuj ponownie</button>
    </div>
  )
}

4. Server Components vs Client Components

W Pages Router komponenty są z natury klienckie. Oznacza to, że nawet po wyrenderowaniu strony na serwerze (SSR/SSG), ich kod JavaScript i tak jest wysyłany do przeglądarki:

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 Server Components, kod zostaje na serwerze:

Code
// app/product/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>
}

To przekłada się na mniejszy JavaScript wysyłany do przeglądarki.

5. Metadata i SEO

Pages Router — komponent 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 — Metadata API z pełnym typowaniem:

Code
// app/about/page.tsx
import { 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 jest czytelniejsze i obsługuje dynamiczne meta tagi bez dodatkowych bibliotek.

Porównanie wydajności

AspektPages RouterApp Router
Bundle sizeWiększy (cały kod do klienta)Mniejszy (Server Components)
Hydration to etap, w którym React podłącza logikę JavaScript do już wygenerowanego HTML-a w przeglądarce.Pełna hydratacjaSelektywna hydratacja
StreamingBrakWbudowany z Suspense
Layout persistenceWymaga obejściaNatywne
Initial loadSzybki (SSR/SSG)Szybki + streaming

Gdzie App Router nadal potrafi boleć

App Router nie zawsze jest idealnym rozwiązaniem dla każdego zespołu i w każdej sytuacji, dlatego też warto bym rozwinął kilka potencjalnych trudności:

  • 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.
  • Złożone zarządzanie danymi: Mechanizmy pamięci podręcznej (cache) i odświeżania danych są bardziej zaawansowane niż w Pages Router, ale też bardziej skomplikowane w obsłudze. Samo użycie fetch() do pobierania danych nie gwarantuje, że zawsze będziesz mieć aktualne informacje.
  • Wyzwania w testowaniu: Narzędzia do testowania i same testy wciąż mogą być problematyczne, zwłaszcza przy asynchronicznych komponentach serwerowych. Testy kompleksowe (E2E) są aktualnie bezpieczniejszym wyborem niż próba testowania wszystkiego testami jednostkowymi.
  • Konieczność nauki nowych koncepcji: Zespół musi przyswoić nowe koncepcje, takie jak React Server Components, strumieniowanie danych, akcje serwerowe i nowe sposoby zarządzania pamięcią podręczną. To wymaga czasu i zmiany sposobu myślenia, a to powoduje duży opór zespołu.

Kiedy wybrać Pages Router?

  • Istniejący projekt — migracja dużego projektu to relatywnie duży wysiłek,
  • Proste strony — landing page, portfolio bez zastosowania złożonej logiki,
  • Zespół nie zna App Routera — krzywa uczenia się jest realna,
  • Zależność od bibliotek — niektóre biblioteki nie wspierają jeszcze Server Components,
  • Stabilność — Pages Router jest pewny i bardzo przewidywalny.

Kiedy wybrać App Router?

  • Nowy projekt — w praktyce to domyślny wybór dla większości nowych aplikacji,
  • Złożone layouty — dashboard, panel admina, aplikacja SaaS,
  • SEO i wydajność — łatwiej wykorzystać streaming, Metadata API i nowy model renderowania,
  • Duże ilości danych — Server Components redukują bundle,
  • Streaming — potrzebujesz pokazywać treść progresywnie,

Pytanie brzmi - czy można używać obu?

Tak, jest taka możliwość, by w Next.js używać hybrydowego podejścia — pages/ i app/ mogą współistnieć, co ułatwia stopniową migrację. Właśnie taka stopniowa migracja, może nam oszczędzić sporo złym emocji, a jednocześnie osiągniemy swój cel.

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

Uwaga! Pamiętaj, że ta sama trasa nie może istnieć w obu folderach (!)

Migracja z Pages Router do App Router

Jeśli masz istniejący projekt, migruj stopniowo:

  1. Utwórz folder app/ i skonfiguruj podstawowy layout.
  2. Zacznij od prostych stron, na "tapetę" weź strony np. about, kontakt, statyczne treści.
  3. Migruj strony z danymi — zamień getStaticProps na komponenty serwerowe async.
  4. Zostaw API routes w pages/api/ albo migruj stopniowo do Route Handlers w app/**/route.ts.
  5. Na końcu migruj te najbardziej złożone strony, czyli dashboardy czy skomplikowane formularze etc.

Nie musisz migrować wszystkiego naraz. Hybrydowe podejście jest w pełni wspierane.

Moja rekomendacja

Nowy projekt w 2026? Najlepszy wybór to właśnie App Router, ponieważ to kierunek rozwoju Next.js i domyślna ścieżka w aktualnej dokumentacji.

Istniejący projekt? Najlepiej zostań przy Pages Router, jeśli działa i zespół nie potrzebuje funkcji specyficznych dla App Router. Nie migruj na siłę, tylko wtedy, gdy realnie uprości to architekturę albo odblokuje nowe możliwości. Z czasem różnica będzie bardziej widoczna między App Router i Pages Router.

Uczysz się Next.js? Zacznij od App Router, konfiguracja projektu z TypeScript i Tailwind to dobry punkt startowy. Pages Router nadal warto rozumieć, bo masa istniejących projektów nadal go używa, ale nowe rzeczy poznawaj już przede wszystkim w modelu App Router.

FAQ

App Router czy Pages Router — co wybrać w 2026?

Dla nowych projektów App Router jest domyślnym i zalecanym wyborem — to aktywnie rozwijana ścieżka Next.js, wspierana przez Vercel i całą dokumentację frameworka. Dla istniejących projektów działających na Pages Router nie ma presji, aby migrować: router jest nadal wspierany, stabilny i nie zostanie usunięty.

Czy Pages Router zostanie usunięty w przyszłości?

Nie, Vercel wielokrotnie potwierdziło, że Pages Router pozostaje w pełni wspierany, ale nie dostanie nowych funkcji. W miare upływu czasu, App Router będzie uzyskiwał coraz wyraźniejszą przewagę, dlatego rozważenie migracji będzie coraz bardziej na miejscu.

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

Tak, Next.js pozwala na hybrydowe podejście, tj foldery pages/ i app/ mogą współistnieć w jednym projekcie, ale trzeba zwrócić uwagę, by ta sama trasa nie istniała w obu folderach. Poza tym, migracja może być stopniowa i przez to "bezbolesna" nowe strony trafiają do app/, stare pozostają w pages/.

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

React Server Components (RSC) to komponenty wykonywane wyłącznie na serwerze, a ich kod nie trafia do bundle'a JavaScriptu wysyłanego do przeglądarki - przekłada się to na mniejszy bundle, znacznie szybsze ładowanie i możliwość bezpośredniego dostępu do bazy danych lub API bez dodatkowego endpointu. App Router używa RSC jako domyślnego modelu, co wymaga innego podejścia do granicy server/client.

Ile zajmuje migracja z Pages Router na App Router?

Zależy od rozmiaru projektu. Prosta strona informacyjna — od kilku godzin do jednego dnia. Złożona aplikacja SaaS z wieloma trasami, niestandardowymi layoutami i zależnościami od bibliotek — od kilku dni do kilku tygodni. Migracja jest inkrementalna (nie trzeba robić wszystkiego naraz), ale wymaga przepisania logiki pobierania danych i przebudowy layoutów.

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/ISR, czyli Incremental Static Regeneration, pozwala odświeżać strony statyczne po czasie bez pełnego rebuildu. może być równie szybki dla większości zastosowań. Różnica jest najbardziej odczuwalna w aplikacjach z dużą ilością danych, złożonymi layoutami i potrzebą streamingu.

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

Część bibliotek CSS-in-JS (styled-components, Emotion) wymaga specjalnej konfiguracji w App Router z powodu Server Components — nie obsługują natywnie trybu serwerowego bez dodatkowych Registry wrapperów. Tailwind CSS, CSS Modules i vanilla CSS działają bez problemów.

Podsumowanie

App Router to nie tylko świetny nowych sposób definiowania tras, ale fundamentalna zmiana w architekturze aplikacji Next.js. Teraz Server Components zamiast Client Components jest ustawiony domyślne, natywne layouty zachowujące stan, wbudowane loading i error states, streaming HTML z Suspense, Metadata API zamiast next/head

Oczywiście Pages Router pozostaje cały czas wspierany i działa - bez nowych funkcji - dla istniejących projektów, ale dla nowych aplikacji App Router oferuje aktualnie bogatszy model renderowania, lepsze API związane z layoutami i metadata oraz wyraźnie skuteczniej pokrywa kierunek rozwoju całego frameworka. Next.js


Planujesz migrację do App Router lub zaczynasz nowy projekt w Next.js? Skontaktuj się ze mną — pomogę wybrać właściwe podejście i uniknąć typowych pułapek.

Pracuję z tym zawodowo.

Jeśli chcesz przełożyć ten temat na lepszą architekturę frontendu, uporządkować React lub Next.js i podnieść jakość pracy zespołu, skontaktuj się ze mną. Pomagam zamieniać wiedzę z artykułów w praktyczne decyzje technologiczne.

O autorze

Maciej Sala

Maciej Sala — project manager i frontendowiec z doświadczeniem w marketingu internetowym. Na co dzień pracuję z Reactem, Next.js i TypeScriptem, łącząc perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat związany z branżą gier wideo jako project manager i game designer.

Absolwent historii na Uniwersytecie Jagiellońskim i studiów podyplomowych z marketingu internetowego na Akademii Górniczo-Hutniczej w Krakowie. Poza pracą trenuje na siłowni, maluje figurki i realizuje własne projekty.

Biblioteka wiedzy

Czytaj dalej

Zobacz więcej wpisów
Astro.js vs Next.js — które narzędzie wybrać w 2026 roku?

Astro.js vs Next.js — które narzędzie wybrać w 2026 roku?

Fachowe porównanie Astro.js i Next.js z perspektywy developera pracującego na co dzień w Next.js. Architektura, wydajność, SEO, DX, koszty i konkretne use case — z benchmarkami i przykładami kodu.

Maciej Sala

Maciej Sala

Founder Strivelab