Parallel Routes i Intercepting Routes w Next.js 16: Praktyczny przewodnik

Praktyczny przewodnik po Parallel Routes i Intercepting Routes w Next.js 16. Buduj modale z deep linking, dashboardy z niezależnymi slotami i poznaj najczęstsze pułapki — z gotowymi przykładami kodu.

Czym są Parallel Routes i dlaczego warto je znać

Ręka do góry — kto nigdy nie budował dashboardu z wieloma panelami, żeby potem walczyć z routingiem przez dobre kilka godzin? Tradycyjne podejście w aplikacjach SPA to ręczne zarządzanie stanem modali, utrata kontekstu przy odświeżeniu strony i zero możliwości udostępnienia linku do konkretnego widoku. Brzmi znajomo?

Parallel Routes i Intercepting Routes w Next.js App Router rozwiązują te problemy na poziomie systemu plików. Zamiast kombinować ze skomplikowaną logiką zarządzania stanem, po prostu definiujesz odpowiednią strukturę folderów — a framework robi resztę.

W tym artykule przejdziemy przez oba mechanizmy od podstaw. Zbudujemy kilka praktycznych przykładów (w tym wzorzec modala z deep linking), a na koniec omówimy najczęstsze pułapki, które potrafią zepsuć nawet doświadczonym programistom cały dzień. Wszystkie przykłady kodu są kompatybilne z Next.js 16 i uwzględniają zmiany z 2026 roku.

Parallel Routes: Wiele widoków w jednym layoucie

Parallel Routes pozwalają jednocześnie renderować wiele niezależnych stron (tzw. slotów) w ramach jednego layoutu. Każdy slot działa jak osobna trasa — ma własny stan nawigacji, własne pliki loading.tsx i error.tsx, a co najważniejsze — może się aktualizować niezależnie od reszty.

Sloty definiujemy konwencją @folder. Folder z prefiksem @ automatycznie staje się slotem i jest przekazywany jako prop do layoutu na tym samym poziomie drzewa katalogów.

Podstawowa struktura katalogów

Wyobraźmy sobie dashboard z trzema panelami — przeglądem, projektami i aktywnością zespołu:

app/
├── dashboard/
│   ├── @overview/
│   │   ├── page.tsx
│   │   ├── loading.tsx
│   │   └── default.tsx
│   ├── @projects/
│   │   ├── page.tsx
│   │   ├── loading.tsx
│   │   └── default.tsx
│   ├── @activity/
│   │   ├── page.tsx
│   │   ├── loading.tsx
│   │   └── default.tsx
│   ├── layout.tsx
│   ├── page.tsx
│   └── default.tsx

Każdy folder @overview, @projects i @activity to slot. Next.js automatycznie przekaże je jako propsy do layoutu:

// app/dashboard/layout.tsx
export default function DashboardLayout({
  children,
  overview,
  projects,
  activity,
}: {
  children: React.ReactNode;
  overview: React.ReactNode;
  projects: React.ReactNode;
  activity: React.ReactNode;
}) {
  return (
    <div className="grid grid-cols-3 gap-6 p-8">
      <section className="col-span-2">
        {children}
        <div className="grid grid-cols-2 gap-4 mt-6">
          {overview}
          {activity}
        </div>
      </section>
      <aside className="col-span-1">
        {projects}
      </aside>
    </div>
  );
}

children to niejawny slot — odpowiada plikowi page.tsx w katalogu dashboard/. Nie musisz tworzyć dla niego folderu @children, ale uwaga — w Next.js 16 musisz mieć plik default.tsx na tym samym poziomie. Łatwo o tym zapomnieć.

Niezależne stany ładowania i błędów

To jest chyba moja ulubiona rzecz w Parallel Routes. Każdy slot może mieć własny loading.tsx i error.tsx, więc kiedy jeden panel ciągnie dane z wolnego API, pozostałe po prostu się renderują:

// app/dashboard/@activity/loading.tsx
export default function ActivityLoading() {
  return (
    <div className="animate-pulse space-y-3">
      <div className="h-4 bg-gray-200 rounded w-3/4"></div>
      <div className="h-4 bg-gray-200 rounded w-1/2"></div>
      <div className="h-4 bg-gray-200 rounded w-2/3"></div>
    </div>
  );
}
// app/dashboard/@activity/error.tsx
'use client';

export default function ActivityError({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    <div className="p-4 bg-red-50 rounded-lg">
      <p className="text-red-600">Nie udało się załadować aktywności</p>
      <button
        onClick={reset}
        className="mt-2 text-sm text-red-500 underline"
      >
        Spróbuj ponownie
      </button>
    </div>
  );
}

To ogromna zmiana w porównaniu z sytuacją, gdy jeden błąd w jednym komponencie wywala biały ekran na całym dashboardzie. Szczerze mówiąc, sam nie raz na to trafiałem.

Warunkowe renderowanie slotów

Parallel Routes świetnie się sprawdzają przy renderowaniu różnych widoków w zależności od roli użytkownika:

// app/dashboard/layout.tsx
import { auth } from '@/lib/auth';

export default async function DashboardLayout({
  children,
  admin,
  user,
}: {
  children: React.ReactNode;
  admin: React.ReactNode;
  user: React.ReactNode;
}) {
  const session = await auth();
  const isAdmin = session?.user?.role === 'admin';

  return (
    <main>
      {children}
      {isAdmin ? admin : user}
    </main>
  );
}

W tym wzorcu sloty @admin i @user zawierają zupełnie inne widoki — admin widzi statystyki i zarządzanie, a zwykły użytkownik swoje dane i ustawienia. Proste i czytelne.

Plik default.tsx: Fundament stabilnego routingu

Okej, teraz porozmawiajmy o default.tsx — bo to prawdopodobnie najczęstsze źródło bólu głowy przy pracy z Parallel Routes. Zrozumienie, kiedy i dlaczego jest potrzebny, zaoszczędzi ci naprawdę sporo czasu.

Kiedy Next.js potrzebuje default.tsx?

Rozróżniamy dwa typy nawigacji:

  • Soft navigation — kliknięcie komponentu <Link>. Next.js pamięta aktywny stan każdego slotu i renderuje go poprawnie.
  • Hard navigation — odświeżenie strony albo wejście bezpośrednio przez URL. Next.js nie zna aktywnego stanu slotów i szuka pliku default.tsx jako fallbacku.

Jeśli default.tsx nie istnieje, Next.js renderuje 404. Co więcej, w Next.js 16 wprowadzono breaking change: brak pliku default.js w dowolnym slocie powoduje błąd budowania. Wcześniej aplikacja po prostu cicho renderowała 404 w runtime — teraz dowiesz się o problemie od razu.

Wzorce implementacji default.tsx

// Najczęstszy wzorzec — zwróć null, gdy slot nie powinien
// nic wyświetlać
// app/dashboard/@modal/default.tsx
export default function ModalDefault() {
  return null;
}
// Alternatywnie — przekieruj na stronę 404
// app/dashboard/@overview/default.tsx
import { notFound } from 'next/navigation';

export default function OverviewDefault() {
  notFound();
}
// Lub wyświetl ten sam widok co page.tsx
// app/dashboard/@projects/default.tsx
export { default } from './page';

Zasada kciuka: dla slotów modalnych — return null. Dla slotów z treścią — re-eksportuj page.tsx lub zwróć sensowny fallback. I nie zapomnij o pliku default.tsx dla niejawnego slotu children!

Intercepting Routes: Przechwytywanie nawigacji

Intercepting Routes pozwalają załadować treść z innej trasy w kontekście bieżącego layoutu — bez zmiany tego, co użytkownik widzi w pasku adresu. To mechanizm, dzięki któremu można budować modale, podglądy, boczne panele i wszelkie widoki typu overlay.

Konwencja nazewnictwa (.)

Intercepting Routes używają notacji nawiasowej, trochę jak ścieżki względne w systemie plików:

  • (.) — przechwytuje trasę na tym samym poziomie segmentów routingu
  • (..) — przechwytuje trasę jeden poziom wyżej
  • (..)(..) — przechwytuje trasę dwa poziomy wyżej
  • (...) — przechwytuje trasę od katalogu głównego app

Ważna uwaga: konwencja (..) bazuje na segmentach routingu, nie na fizycznej strukturze katalogów. Foldery parallel route (@slot) nie liczą się jako segmenty. To częste źródło błędów — serio, sprawdź to dwa razy, zanim zaczniesz debugować coś innego.

Jak działa przechwytywanie w praktyce

Rozważmy galerię zdjęć. Chcemy, żeby:

  1. Kliknięcie zdjęcia na liście otwierało je w modalu (bez pełnej nawigacji)
  2. Bezpośrednie wejście na /photo/123 renderowało pełną stronę zdjęcia
  3. Odświeżenie strony z otwartym modalem pokazywało pełną stronę

Struktura katalogów:

app/
├── @modal/
│   ├── (.)photo/
│   │   └── [id]/
│   │       └── page.tsx    ← widok w modalu
│   └── default.tsx         ← zwraca null
├── photo/
│   └── [id]/
│       └── page.tsx        ← pełna strona zdjęcia
├── layout.tsx
├── page.tsx                ← lista zdjęć
└── default.tsx

Gdy użytkownik klika <Link href="/photo/123">, Next.js przechwytuje nawigację i zamiast renderować pełną stronę, ładuje komponent z @modal/(.)photo/[id]/page.tsx w slocie @modal. URL w przeglądarce zmienia się na /photo/123, ale layout pozostaje — lista zdjęć wciąż jest widoczna pod modalem.

Natomiast przy bezpośrednim wejściu na /photo/123 (wpisanie URL-a, odświeżenie, link z zewnątrz) — przechwycenie nie zachodzi i renderuje się normalny photo/[id]/page.tsx.

Budujemy modal z deep linking — krok po kroku

No dobra, czas na mięso. Połączenie Parallel Routes z Intercepting Routes daje nam modale, które:

  • Mają własny URL — można je udostępnić linkiem
  • Zamykają się przy nawigacji wstecz
  • Zachowują kontekst strony w tle
  • Po odświeżeniu renderują pełną stronę zamiast modala

Krok 1: Komponent Modal

Zacznijmy od reużywalnego komponentu modala:

// components/Modal.tsx
'use client';

import { useRouter } from 'next/navigation';
import { useCallback, useEffect, useRef } from 'react';

export default function Modal({
  children,
}: {
  children: React.ReactNode;
}) {
  const router = useRouter();
  const overlayRef = useRef<HTMLDivElement>(null);

  const onDismiss = useCallback(() => {
    router.back();
  }, [router]);

  // Zamknij modal klawiszem Escape
  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onDismiss();
    };
    document.addEventListener('keydown', handleKeyDown);
    return () => document.removeEventListener('keydown', handleKeyDown);
  }, [onDismiss]);

  // Zamknij modal po kliknięciu w tło
  const handleOverlayClick = (e: React.MouseEvent) => {
    if (e.target === overlayRef.current) onDismiss();
  };

  return (
    <div
      ref={overlayRef}
      onClick={handleOverlayClick}
      className="fixed inset-0 z-50 flex items-center justify-center bg-black/60"
    >
      <div className="relative w-full max-w-2xl rounded-xl bg-white p-6 shadow-xl">
        <button
          onClick={onDismiss}
          className="absolute right-4 top-4 text-gray-400 hover:text-gray-600"
          aria-label="Zamknij"
        >
          ✕
        </button>
        {children}
      </div>
    </div>
  );
}

Kluczowa sprawa — modal zamyka się przez router.back(). Nawigacja wstecz usuwa trasę przechwytującą ze stosu, co odmontowuje slot @modal. Eleganckie, prawda?

Krok 2: Layout z slotem @modal

// app/layout.tsx
export default function RootLayout({
  children,
  modal,
}: {
  children: React.ReactNode;
  modal: React.ReactNode;
}) {
  return (
    <html lang="pl">
      <body>
        {children}
        {modal}
      </body>
    </html>
  );
}

Krok 3: Strona galerii

// app/page.tsx
import Link from 'next/link';

const photos = [
  { id: 1, title: 'Zachód słońca', src: '/photos/sunset.jpg' },
  { id: 2, title: 'Góry', src: '/photos/mountains.jpg' },
  { id: 3, title: 'Ocean', src: '/photos/ocean.jpg' },
];

export default function GalleryPage() {
  return (
    <main className="p-8">
      <h1 className="text-3xl font-bold mb-8">Galeria zdjęć</h1>
      <div className="grid grid-cols-3 gap-4">
        {photos.map((photo) => (
          <Link
            key={photo.id}
            href={`/photo/${photo.id}`}
            className="group relative aspect-square overflow-hidden rounded-lg"
          >
            <img
              src={photo.src}
              alt={photo.title}
              className="h-full w-full object-cover transition-transform group-hover:scale-105"
            />
            <span className="absolute bottom-2 left-2 text-white font-medium">
              {photo.title}
            </span>
          </Link>
        ))}
      </div>
    </main>
  );
}

Uwaga: musisz używać komponentu <Link> z Next.js — zwykły tag <a> wywoła hard navigation i przechwycenie nie zadziała. To jeden z tych błędów, które łatwo przegapić.

Krok 4: Przechwycona trasa (widok w modalu)

// app/@modal/(.)photo/[id]/page.tsx
import Modal from '@/components/Modal';

export default async function PhotoModal({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;

  // Pobierz dane zdjęcia (Server Component)
  const photo = await getPhoto(id);

  return (
    <Modal>
      <img
        src={photo.src}
        alt={photo.title}
        className="w-full rounded-lg"
      />
      <h2 className="mt-4 text-xl font-semibold">{photo.title}</h2>
      <p className="mt-2 text-gray-600">{photo.description}</p>
    </Modal>
  );
}

Zwróć uwagę na typ params: Promise<{ id: string }> i await params. Od Next.js 16 parametry routingu są wyłącznie asynchroniczne — synchroniczny dostęp został całkowicie usunięty. Dotyczy to page.tsx, layout.tsx, default.tsx i route.tsx.

Krok 5: Pełna strona zdjęcia (fallback dla hard navigation)

// app/photo/[id]/page.tsx
import Link from 'next/link';

export default async function PhotoPage({
  params,
}: {
  params: Promise<{ id: string }>;
}) {
  const { id } = await params;
  const photo = await getPhoto(id);

  return (
    <main className="flex min-h-screen items-center justify-center bg-gray-100 p-8">
      <article className="max-w-3xl">
        <Link
          href="/"
          className="mb-4 inline-block text-blue-600 hover:underline"
        >
          ← Powrót do galerii
        </Link>
        <img
          src={photo.src}
          alt={photo.title}
          className="w-full rounded-xl shadow-lg"
        />
        <h1 className="mt-6 text-3xl font-bold">{photo.title}</h1>
        <p className="mt-4 text-gray-700 leading-relaxed">
          {photo.description}
        </p>
      </article>
    </main>
  );
}

Krok 6: Pliki default.tsx

// app/@modal/default.tsx
export default function ModalDefault() {
  return null;
}
// app/default.tsx
export { default } from './page';

Te pliki są absolutnie konieczne. Bez app/@modal/default.tsx odświeżenie strony na /photo/123 spowoduje błąd budowania w Next.js 16 (lub 404 we wcześniejszych wersjach). Nie pomijaj ich.

Zaawansowane wzorce: Dashboard z wieloma slotami

Modale to najpopularniejszy przypadek użycia, ale Parallel Routes potrafią znacznie więcej. Oto wzorzec dashboardu analitycznego, gdzie różne sekcje ciągną dane z różnych API:

// app/analytics/layout.tsx
import { Suspense } from 'react';

export default function AnalyticsLayout({
  children,
  revenue,
  users,
  events,
}: {
  children: React.ReactNode;
  revenue: React.ReactNode;
  users: React.ReactNode;
  events: React.ReactNode;
}) {
  return (
    <div className="min-h-screen bg-gray-50">
      <header className="bg-white border-b px-8 py-4">
        <h1 className="text-2xl font-bold">Panel analityczny</h1>
      </header>

      <div className="p-8 space-y-6">
        {/* Główna treść */}
        {children}

        {/* Sloty renderowane równolegle z niezależnym
            ładowaniem i obsługą błędów */}
        <div className="grid grid-cols-3 gap-6">
          <Suspense fallback={<CardSkeleton />}>
            {revenue}
          </Suspense>
          <Suspense fallback={<CardSkeleton />}>
            {users}
          </Suspense>
          <Suspense fallback={<CardSkeleton />}>
            {events}
          </Suspense>
        </div>
      </div>
    </div>
  );
}

function CardSkeleton() {
  return (
    <div className="animate-pulse bg-white rounded-xl p-6 h-48">
      <div className="h-4 bg-gray-200 rounded w-1/3 mb-4"></div>
      <div className="h-8 bg-gray-200 rounded w-2/3"></div>
    </div>
  );
}

Każdy slot (@revenue, @users, @events) to Server Component pobierający dane niezależnie. React streamuje gotowe fragmenty do przeglądarki — użytkownik widzi wyniki stopniowo, bez czekania na najwolniejsze zapytanie. W praktyce to daje naprawdę odczuwalną różnicę w UX.

Intercepting Routes od katalogu głównego: Globalne modale

Konwencja (...) pozwala przechwycić trasę z dowolnego miejsca w aplikacji, od poziomu katalogu app/. Idealne rozwiązanie dla globalnych modali — logowanie, rejestracja, koszyk zakupowy:

app/
├── @modal/
│   ├── (...)login/
│   │   └── page.tsx       ← modal logowania
│   ├── (...)cart/
│   │   └── page.tsx       ← modal koszyka
│   └── default.tsx
├── login/
│   └── page.tsx           ← pełna strona logowania
├── cart/
│   └── page.tsx           ← pełna strona koszyka
├── products/
│   └── page.tsx           ← może zawierać Link do /cart
├── layout.tsx
└── default.tsx

Teraz <Link href="/login"> z dowolnego miejsca w aplikacji otworzy modal logowania, a bezpośrednie wejście na /login wyświetli pełną stronę. Jeden kod, dwa doświadczenia użytkownika — całkiem sprytne.

Rozwiązywanie problemów: Najczęstsze pułapki

Po kilku projektach z Parallel Routes i Intercepting Routes mogę szczerze powiedzieć, że pewne problemy powtarzają się niemal za każdym razem. Oto lista pułapek, na które prawdopodobnie trafisz (i jak je naprawić).

Problem 1: Błąd 404 po odświeżeniu strony

Objaw: Modal działa po kliknięciu linku, ale po odświeżeniu strony widzisz 404.

Przyczyna: Brak pliku default.tsx w slocie parallel route.

Rozwiązanie: Dodaj default.tsx do każdego slotu @folder oraz do katalogu z niejawnym slotem children.

Problem 2: Modal nie znika po nawigacji

Objaw: Po przejściu na inną stronę treść modala wciąż jest widoczna.

Przyczyna: Layout nie renderuje się ponownie przy nawigacji — slot @modal zachowuje ostatni stan.

Rozwiązanie: Użyj catch-all route w slocie, który zwraca null:

// app/@modal/[...catchAll]/page.tsx
export default function CatchAllModal() {
  return null;
}

Problem 3: Przechwytywanie nie działa — renderuje się pełna strona

Objaw: Kliknięcie <Link> otwiera pełną stronę zamiast modala.

Możliwe przyczyny:

  • Używasz tagu <a> zamiast komponentu <Link> z Next.js
  • Źle policzyłeś poziomy w konwencji (..) — pamiętaj, foldery @slot nie liczą się jako segmenty
  • Serwer deweloperski wymaga restartu po dodaniu nowych slotów

Problem 4: Dwa modale wyświetlają się jednocześnie

Objaw: Przy nawigacji między modalami oba są widoczne naraz.

Rozwiązanie: Umieść wszystkie intercepting routes w jednym slocie parallel route. Osobne sloty (@loginModal, @cartModal) dla różnych modali spowodują, że oba renderują się równolegle — bo to dokładnie to, co Parallel Routes robią.

Problem 5: Nowy slot nie jest wykrywany w dev serverze

Objaw: Dodałeś nowy folder @slot, ale hot reload go nie łapie.

Rozwiązanie: Zatrzymaj i uruchom ponownie serwer deweloperski. To znany problem z cache — rm -rf .next && npm run dev powinno załatwić sprawę.

Problem 6: Statyczne i dynamiczne sloty w jednym layoucie

Objaw: Strona, która powinna być generowana statycznie, przełącza się na dynamiczne renderowanie.

Przyczyna: Jeśli choćby jeden slot w layoucie jest dynamiczny, wszystkie sloty na tym samym poziomie stają się dynamiczne.

Rozwiązanie: Oddziel dynamiczne sloty do osobnego, zagnieżdżonego layoutu.

Zmiany w Next.js 16 dotyczące Parallel Routes

Next.js 16 (aktualnie najnowsza stabilna wersja) wprowadził kilka ważnych zmian, o których warto wiedzieć:

  • Wymagany plik default.js — wszystkie sloty muszą mieć jawny default.js. Brak = błąd budowania (nie cichy 404 jak kiedyś).
  • Asynchroniczne parametryparams w page.tsx, layout.tsx, default.tsx i route.tsx są dostępne wyłącznie przez await. Synchroniczny dostęp usunięto całkowicie.
  • Automatyczne typowanie slotów (od 15.5) — LayoutProps automatycznie uwzględnia sloty parallel route, więc ręczna deklaracja typów nie jest już konieczna.
  • Per-segment prefetching — ulepszone prefetchowanie z lepszym ponownym wykorzystaniem cache, co poprawia wydajność nawigacji między slotami.

Jeśli migrujesz z Next.js 15, skorzystaj z oficjalnego codemod: npx @next/codemod@canary upgrade latest.

Kiedy stosować Parallel Routes, a kiedy nie

Parallel Routes to potężne narzędzie, ale nie zawsze potrzebne. Oto szybki przegląd:

Sięgaj po Parallel Routes, gdy:

  • Potrzebujesz niezależnych stanów ładowania i błędów dla różnych sekcji strony
  • Chcesz warunkowo renderować całe sekcje na podstawie danych użytkownika
  • Budujesz modale z deep linking (w połączeniu z Intercepting Routes)
  • Dashboard ma panele pobierające dane z różnych źródeł

Odpuść sobie, gdy:

  • Prosty modal bez potrzeby URL-a — wystarczy useState
  • Proste zakładki bez niezależnego ładowania — searchParams zrobią robotę
  • Layout z sidebarem bez niezależnej nawigacji — zwykłe zagnieżdżone layouty wystarczą

Często zadawane pytania

Czym się różnią Parallel Routes od zwykłych zagnieżdżonych layoutów?

Zagnieżdżone layouty renderują jedną aktywną stronę w danym momencie — layout opakowuje children, a children to zawsze jeden komponent. Parallel Routes pozwalają renderować wiele niezależnych stron jednocześnie, z osobnymi stanami ładowania, błędów i nawigacji. To fundamentalna różnica — layouty to hierarchia, Parallel Routes to kompozycja równoległa.

Czy Intercepting Routes działają z tagiem <a> zamiast komponentu Link?

Nie. Intercepting Routes wymagają soft navigation, czyli nawigacji po stronie klienta przez <Link> lub router.push(). Zwykły <a> wywołuje hard navigation (pełne przeładowanie), więc przechwycenie nie zachodzi. To samo dotyczy wpisania URL-a w przeglądarkę lub odświeżenia strony.

Jak prawidłowo liczyć poziomy w konwencji (..) dla Intercepting Routes?

Konwencja (..) odnosi się do segmentów routingu, nie fizycznych folderów. Foldery @modal, @sidebar oraz grupy tras jak (marketing) czy (auth) nie są liczone jako segmenty. Liczą się tylko foldery tworzące rzeczywiste segmenty URL. W razie wątpliwości — porównaj segmenty URL obu tras. To najbezpieczniejsza metoda.

Dlaczego po odświeżeniu strony zamiast modala widzę pełną stronę lub błąd 404?

To zamierzone zachowanie. Intercepting Routes działają wyłącznie podczas soft navigation. Odświeżenie to hard navigation — Next.js nie może odtworzyć kontekstu przechwycenia i renderuje normalną trasę. Błąd 404 oznacza brak default.tsx. W Next.js 16 brak tego pliku to błąd budowania, co w sumie jest lepsze — dowiadujesz się o problemie wcześniej.

Czy mogę używać Parallel Routes w Server Components bez utraty wydajności?

Tak, i szczerze mówiąc to jeden z głównych powodów ich istnienia. Sloty to domyślnie Server Components — dane pobierasz na serwerze, React streamuje gotowe fragmenty do klienta. Jedno zastrzeżenie: jeśli dowolny slot jest dynamiczny, wszystkie sloty w tym samym layoucie przełączają się na renderowanie dynamiczne. Warto o tym pamiętać przy planowaniu struktury.

O Autorze Editorial Team

Our team of expert writers and editors.