# Next.js App Router Standard

> **Scope:** frontend/nextjs/app-router
> **Layer:** 2 (on keyword)
> **Keywords:** next.js, app router, server component, client component, page, layout, route
> **Load When:** editing files in `app/` or `features/` directories

**Verified against:** Next.js 15. Last-verified: 2026-05-20.

---

Next.js App Router with TypeScript strict mode. Server Components by default, Client Components only when interactivity is required.

## Core Rules

- ALWAYS default to Server Components — add `'use client'` only when needed
- ALWAYS keep `app/` directory for routing only — no business logic in page files
- NEVER put data fetching in Client Components when a Server Component can do it
- NEVER use `useEffect` to fetch data — use Server Components or TanStack Query
- ALWAYS co-locate loading/error UI: `loading.tsx`, `error.tsx` next to `page.tsx`
- ALWAYS use TypeScript — no `.js` or `.jsx` files in the project

## Server vs Client Components

| Use Server Component | Use Client Component |
|---------------------|---------------------|
| Fetching from .NET API on load | onClick, onChange, form submit |
| Rendering static or user-specific data | useState, useEffect, useRef |
| Accessing backend environment variables | Browser APIs (localStorage, geolocation) |
| SEO-critical content | TanStack Query hooks |
| No interactivity needed | shadcn/ui interactive components |

## Decision Tree

```
Does this component need user interaction (click, input, hover state)?
  YES → 'use client'
  NO  → Does it fetch data?
          YES → Server Component (fetch directly)
          NO  → Server Component (static)
```

## App Directory Structure

```
src/app/
├── (auth)/
│   ├── login/
│   │   └── page.tsx           # Server Component — renders login form
│   └── register/
│       └── page.tsx
├── (dashboard)/
│   ├── layout.tsx             # Shared layout for dashboard routes
│   ├── page.tsx               # Dashboard home
│   └── users/
│       ├── page.tsx           # User list (Server Component)
│       ├── [id]/
│       │   └── page.tsx       # User detail
│       ├── loading.tsx        # Suspense fallback
│       └── error.tsx          # Error boundary
└── layout.tsx                 # Root layout — providers, fonts, metadata
```

## File Conventions

| File | Purpose | Type |
|------|---------|------|
| `page.tsx` | Route segment UI | Server Component (default) |
| `layout.tsx` | Shared UI wrapper | Server Component |
| `loading.tsx` | Suspense fallback | Server Component |
| `error.tsx` | Error boundary | **Must be Client Component** |
| `not-found.tsx` | 404 page | Server Component |
| `route.ts` | API route (avoid — use .NET API) | — |

## Server Component Data Fetch Pattern

Server Components call the .NET backend directly — use a dedicated `BACKEND_API_PREFIX` constant, never
the same-origin proxy prefix Client Components use. See `frontend/nextjs/data-fetching.md` →
"Common Mistakes" and `backend/integrations/neon-auth/neon-auth.md` → "API Prefix Convention".

```tsx
// app/(dashboard)/users/page.tsx
import { UserList } from '@/features/users/components/user-list';
import { BACKEND_API_PREFIX } from '@/lib/api/prefixes'; // server-direct prefix ONLY

async function getUsers(): Promise<User[]> {
  const res = await fetch(`${BACKEND_API_PREFIX}/users`, {
    next: { revalidate: 60 },
  });
  if (!res.ok) throw new Error('Failed to fetch users');
  return res.json();
}

export default async function UsersPage() {
  const users = await getUsers();
  return <UserList initialUsers={users} />;
}
```

## Root Layout — Required Providers

```tsx
// app/layout.tsx
import { QueryProvider } from '@/lib/query-client';
import { Toaster } from '@/components/ui/sonner';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="pt-BR">
      <body>
        <QueryProvider>
          {children}
          <Toaster />
        </QueryProvider>
      </body>
    </html>
  );
}
```

## Common Mistakes

| Wrong | Right | Why |
|-------|-------|-----|
| `'use client'` on every component | Only add when needed | Kills SSR benefits, bloats JS bundle |
| `useEffect(() => { fetch(...) }, [])` | Server Component fetch or TanStack Query | Two renders, no caching, no SSR |
| Business logic in `page.tsx` | Move to `features/` | pages are routes, not controllers |
| `fetch` without error handling | Always check `res.ok` | Silent failures in production |

---

*MORPH-SPEC by Polymorphism Tech*
