Next.js App Router zmienił model mentalny dla aplikacji React. Server Components, streaming, parallel routes, intercepting routes — powierzchnia jest ogromna, a narzędzia AI czasem generują wzorce Pages Router przez pomyłkę. Te przepisy generują kod App Router wyłącznie, z prawidłowymi granicami serwer/klient i właściwymi wzorcami danych.
Przepisy Server Component i Server Action z prawidłowymi granicami
Wzorce pobierania danych używające RSC, streaming i Suspense
Przepisy uwierzytelniania i middleware dla App Router
Prompty optymalizacji wydajności dla Core Web Vitals
Scenariusz: Twoja strona pulpitu wykonuje 4 niezależne wywołania API. Wszystkie blokują renderowanie, dopóki najwolniejsze się nie zakończy.
Wskazówka
Zrefaktoryzuj app/dashboard/page.tsx, aby używać streamingu z granicami Suspense. Strona jest Server Component (bez “use client”). Wykonuje 4 niezależne pobierania danych: getStats(), getRecentOrders(), getTopProducts(), getActivityFeed(). Owinąć każdą sekcję zależną od danych w własną granicę <Suspense fallback={<SectionSkeleton />}>. Utwórz komponent asynchroniczny dla każdej sekcji, który pobiera własne dane: StatsSection, RecentOrdersSection, TopProductsSection, ActivityFeedSection. Powłoka strony renderuje się natychmiast. Każda sekcja streamuje się w miarę rozwiązywania danych. NIE używaj Promise.all — każda sekcja jest niezależna. Utwórz dopasowane komponenty szkieletowe pasujące do wymiarów końcowego layoutu, aby zapobiec przesunięciu layoutu. Dodaj właściwe metadane używając eksportu generateMetadata.
Oczekiwany wynik: Strona z 4 granicami Suspense, 4 komponentami asynchronicznymi sekcji, 4 szkieletami i metadane.
Scenariusz: Potrzebujesz formularza kontaktowego walidującego po stronie serwera, obsługującego błędy i pokazującego stan oczekiwania bez osobnej trasy API.
Wskazówka
Utwórz formularz kontaktowy z Server Actions. (1) app/contact/actions.ts z “use server”: akcja submitContactForm przyjmująca FormData, walidująca z Zod (name wymagane, email prawidłowy, message 10-1000 znaków, honeypot pusty), zapisująca do bazy danych, wysyłająca email przez Resend, zwracająca sukces lub błędy na poziomie pola. (2) app/contact/page.tsx jako Server Component zawierający formularz. (3) app/contact/contact-form.tsx jako Client Component używający useFormState dla akcji i błędów pól, useFormStatus dla stanu przycisku submit, progresywne wzmocnienie, aby formularz działał bez JavaScript. (4) Po sukcesie, pokaż wiadomość i zresetuj formularz. (5) Rate limit: 5 zgłoszeń na godzinę na IP używając KV store. Testuj z prawidłowymi danymi, błędami walidacji i przekroczonym limitem.
Oczekiwany wynik: Akcja serwera, komponent strony, komponent formularza klienta i testy.
Scenariusz: Potrzebujesz uwierzytelniania email/hasło i OAuth z zarządzaniem sesją i chronionymi trasami.
Wskazówka
Skonfiguruj NextAuth.js v5 z App Router. (1) Utwórz auth.ts z konfiguracją NextAuth(): provider Credentials (email/hasło z bcrypt), Google, GitHub. Adapter Drizzle dla sesji DB. JWT z 30-dniowym maxAge. Callbacki: jwt zawiera userId i role, session je eksponuje. (2) middleware.ts chroniący /dashboard/, /admin/ , /api/* oprócz /api/auth/*. (3) Handler tras w app/api/auth/[...nextauth]/route.ts. (4) Typowany wrapper useSession w lib/auth-client.ts. (5) Client Component LoginForm z email/hasłem i przyciskami OAuth. (6) UserMenu z avatarem, nazwą, wylogowaniem. (7) HOC withAuth(Component, { role? }) dla sprawdzania auth po stronie serwera. Testuj przepływ logowania, persystencję sesji, przekierowania chronionych i dostęp oparty na roli.
Oczekiwany wynik: Konfiguracja auth, middleware, handler tras, narzędzia klienta, komponenty, HOC i testy.
Scenariusz: Szczegóły produktu powinny otwierać się w modalu z listy, ale pokazywać pełną stronę przy bezpośrednim dostępie.
Wskazówka
Zaimplementuj intercepting routes dla modalu produktu. (1) app/products/page.tsx — Server Component listujący produkty w siatce linkującej do /products/[id]. (2) app/products/[id]/page.tsx — pełna strona szczegółów dla bezpośredniego dostępu. (3) app/products/@modal/(.)products/[id]/page.tsx — intercepting route renderująca te same dane wewnątrz modalu. (4) app/products/layout.tsx renderujący children i modal jako sloty równoległe. (5) ProductModal.tsx Client Component z kliknięciem tła do zamknięcia (router.back()), klawiszem Escape, pułapką fokusa i linkiem “Zobacz pełną stronę”. (6) Obsługuj nawigację prawidłowo: router.back() wraca do listy, przycisk wstecz przeglądarki zamyka modal, odświeżenie pokazuje pełną stronę. Testuj wszystkie scenariusze nawigacji.
Oczekiwany wynik: Strony produktów, intercepting route, layout ze slotami równoległymi, komponent modalu i testy.
Scenariusz: Potrzebujesz niskopóźnieniowych tras API działających na edge z typowaniem, walidacją i cache’owaniem.
Wskazówka
Utwórz handlery tras API w app/api/products/. (1) route.ts z GET (lista z paginacją, filtrowaniem, sortowaniem) i POST (tworzenie z walidacją Zod). (2) [id]/route.ts z GET, PUT, DELETE (soft-delete). (3) Dodaj export const runtime = 'edge'. (4) Utwórz lib/api-utils.ts z helperami: validateBody<T>(schema, request), paginatedResponse(data, total, page, pageSize), apiError(status, message, details?). (5) Dodaj sprawdzanie auth przez auth() z NextAuth. (6) Żądania GET zwracają Cache-Control z stale-while-revalidate. POST/PUT/DELETE czyszczą tagi cache z revalidateTag(). (7) Dodaj komentarze JSDoc @swagger. Testuj każdy endpoint z prawidłowymi żądaniami, błędami walidacji, awariami auth i not-found.
Oczekiwany wynik: Handlery tras, biblioteka narzędzi API i testy dla każdego endpointu.
Scenariusz: Twój blog ma 500 postów. Pełna generacja statyczna trwa 10 minut. Potrzebujesz ISR.
Wskazówka
Zaimplementuj ISR. (1) W app/blog/page.tsx, pobieraj z { next: { revalidate: 3600 } } dla 1-godzinnego cache. (2) W app/blog/[slug]/page.tsx, użyj generateStaticParams() dla 50 najnowszych postów. Ustaw dynamicParams = true i revalidate = 86400. (3) Utwórz handler webhooka app/api/revalidate/route.ts wywołujący revalidatePath i revalidateTag, gdy CMS publikuje. (4) Dodaj tagi fetch: { next: { tags: ['post-' + slug, 'blog-posts'] } }. (5) Generuj sitemap w app/sitemap.ts używając MetadataRoute.Sitemap. (6) Dodaj opengraph-image.tsx dla dynamicznych obrazów OG używając ImageResponse z next/og. Testuj, że webhook rewalidacji wyzwala czyszczenie cache.
Oczekiwany wynik: Strony bloga z ISR, webhook rewalidacji, sitemap, generowanie obrazu OG i testy.
Scenariusz: Podziel ruch między wariantami strony docelowej i bramkuj funkcje za flagami na edge.
Wskazówka
Utwórz middleware.ts dla testów A/B i flag funkcji. (1) A/B: sprawdź cookie ab-variant, przypisz losowo jeśli brakuje, ustaw na 30 dni. Przepisz /pricing do /pricing/variant-a lub variant-b niewidocznie. (2) Flagi funkcji: pobierz z KV kluczowane przez ID użytkownika/anonimowego, ustaw jako nagłówek żądania x-feature-flags dla Server Components. (3) Geolokalizacja: odczytaj request.geo, przekieruj / do /en lub /es respektując cookie języka. (4) Wykrywanie botów: sprawdź User-Agent, ustaw nagłówek x-is-bot dla pomijania drogich personalizacji. (5) Utwórz komponowalne funkcje middleware w lib/middleware-utils.ts. (6) Dodaj config.matcher do pomijania zasobów statycznych. Testuj każde zachowanie z mockowanym NextRequest.
Oczekiwany wynik: Middleware, funkcje narzędziowe, katalogi wariantów i testy.
Scenariusz: Każda strona potrzebuje unikalnych meta tagów, dynamicznych obrazów OG i danych strukturalnych JSON-LD.
Wskazówka
Utwórz system metadanych. (1) W app/layout.tsx, ustaw domyślne metadane: title, description, obraz OG, karta Twitter, robots. (2) Utwórz lib/metadata.ts z generatePageMetadata(options) zwracającym Metadata z szablonem tytułu, opisem, kanonicznym URL, obrazem OG, alternates i JSON-LD. (3) Dla bloga: generateMetadata pobierający post, zwracający tytuł, opis wyciągu, dynamiczny obraz OG z opengraph-image.tsx (renderowany Satori), dane strukturalne artykułu. (4) Dla produktów: dane strukturalne Product z ceną, dostępnością, oceną. (5) Utwórz app/robots.ts i app/sitemap.ts. (6) Weryfikuj brak duplikatów tagów, właściwe kanoniczne, prawidłowy JSON-LD.
Oczekiwany wynik: Narzędzie metadanych, generowanie obrazu OG, helpery JSON-LD, robots.ts, sitemap.ts i testy.
Scenariusz: Wynik wydajności Lighthouse to 65. LCP 4.2s, CLS 0.25, FID 180ms.
Wskazówka
Napraw Core Web Vitals. (1) LCP: Konwertuj obrazy hero na next/image z priority, wyraźnymi wymiarami, atrybutem sizes. Preload obrazu LCP w metadanych. (2) CLS: Dodaj wyraźne wymiary do wszystkich obrazów/embedów. Użyj szkieletów Suspense pasujących do końcowych wymiarów. Napraw czcionki z next/font display:swap i preload. (3) INP: Przenieś filtrowanie/sortowanie do useDeferredValue lub useTransition. Podziel duże Client Components z dynamicznymi importami i ssr:false. (4) Ogólne: Włącz PPR jeśli dostępne. Użyj Script strategy=“lazyOnload” dla analityki. Konfiguruj obrazy: { formats: ['image/avif', 'image/webp'] }. (5) Skonfiguruj raportowanie web-vitals. Dodaj Lighthouse CI w GitHub Actions z budżetami: LCP < 2.5s, CLS < 0.1, INP < 200ms.
Oczekiwany wynik: Zoptymalizowane komponenty, zmiany konfiguracji, setup czcionek, workflow Lighthouse CI i metryki.
Scenariusz: Twój SaaS obsługuje wielu najemców z pojedynczego wdrożenia z routingiem opartym na subdomenach i izolacją danych.
Wskazówka
Zaimplementuj multi-tenancy. (1) W middleware.ts, ekstrahuj tenant z subdomeny, waliduj w KV, ustaw jako nagłówek x-tenant-id. (2) Utwórz lib/tenant.ts z getTenant() czytającym nagłówek w Server Components. (3) Utwórz TenantProvider Client Component dostarczający kontekst tenant (nazwa, motyw, logo, funkcje). (4) W warstwie bazy danych, każde zapytanie zawiera WHERE tenant_id. Utwórz wrapper scopedQuery(tenantId) dla Drizzle. (5) Tematyzacja specyficzna dla najemcy przez zmienne CSS ustawione w root layout. (6) Obsługuj brakującą subdomenę tenant z brandowanym 404. Obsługuj www i naked domain jako stronę marketingową. Testuj izolację tenant: zapytania nigdy nie zwracają danych innych najemców.
Oczekiwany wynik: Middleware, narzędzie tenant, provider, zapytania scopowane, tematyczny layout i testy izolacji.
Scenariusz: Twój pulpit potrzebuje wtyczek instalowanych przez użytkownika dodających strony, trasy API i komponenty UI.
Wskazówka
Zbuduj system wtyczek. (1) Zdefiniuj interfejs Plugin: id, name, version, routes, apiRoutes, sidebarItems, settingsComponent. (2) Utwórz PluginRegistry ładujący wtyczki z bazy danych. (3) Catch-all route app/plugins/[pluginId]/[...path]/page.tsx dynamicznie ładująca komponenty wtyczek. (4) Catch-all API app/api/plugins/[pluginId]/[...path]/route.ts delegująca do handlerów wtyczek. (5) Sidebar czyta aktywne wtyczki i wstrzykuje elementy. (6) Przepływ instalacji: upload pakietu, waliduj manifest, przechowuj, zarejestruj. (7) Sandbox: granica błędu per wtyczka, scopowany dostęp API. Testuj ładowanie, delegację i izolację.
Oczekiwany wynik: Interfejs wtyczki, rejestr, catch-all routes, integracja sidebar, przepływ instalacji i testy.
Scenariusz: Twoje 20 endpointów API ma typy zawsze niezsynchronizowane z frontendem.
Wskazówka
Skonfiguruj tRPC v11 z App Router. (1) server/trpc.ts z kontekstem (sesja, baza danych), routerem, middleware (isAuthed, isAdmin). (2) server/routers/ z users, posts (paginacja kursora), sub-routery comments. Każda procedura ma walidację wejścia Zod. (3) Handler app/api/trpc/[trpc]/route.ts. (4) lib/trpc-client.ts z typowanym klientem. (5) lib/trpc-react.ts z integracją @trpc/react-query. (6) W komponentach, użyj trpc.posts.list.useQuery i trpc.posts.create.useMutation — w pełni typowane z routera z zerowymi manualnymi definicjami typów po stronie klienta. (7) Dodaj subskrypcje dla real-time. Testuj: zmiana typu wyjścia routera powoduje błędy TypeScript w komponentach.
Oczekiwany wynik: Setup tRPC, routery, handler, typowany klient, integracja React Query i weryfikacja bezpieczeństwa typów.