| 1 | # File Conventions |
| 2 | |
| 3 | Next.js App Router uses file-based routing with special file conventions. |
| 4 | |
| 5 | ## Project Structure |
| 6 | |
| 7 | Reference: https://nextjs.org/docs/app/getting-started/project-structure |
| 8 | |
| 9 | ``` |
| 10 | app/ |
| 11 | ├── layout.tsx # Root layout (required) |
| 12 | ├── page.tsx # Home page (/) |
| 13 | ├── loading.tsx # Loading UI |
| 14 | ├── error.tsx # Error UI |
| 15 | ├── not-found.tsx # 404 UI |
| 16 | ├── global-error.tsx # Global error UI |
| 17 | ├── route.ts # API endpoint |
| 18 | ├── template.tsx # Re-rendered layout |
| 19 | ├── default.tsx # Parallel route fallback |
| 20 | ├── blog/ |
| 21 | │ ├── page.tsx # /blog |
| 22 | │ └── [slug]/ |
| 23 | │ └── page.tsx # /blog/:slug |
| 24 | └── (group)/ # Route group (no URL impact) |
| 25 | └── page.tsx |
| 26 | ``` |
| 27 | |
| 28 | ## Special Files |
| 29 | |
| 30 | | File | Purpose | |
| 31 | | --------------- | ---------------------------------------- | |
| 32 | | `page.tsx` | UI for a route segment | |
| 33 | | `layout.tsx` | Shared UI for segment and children | |
| 34 | | `loading.tsx` | Loading UI (Suspense boundary) | |
| 35 | | `error.tsx` | Error UI (Error boundary) | |
| 36 | | `not-found.tsx` | 404 UI | |
| 37 | | `route.ts` | API endpoint | |
| 38 | | `template.tsx` | Like layout but re-renders on navigation | |
| 39 | | `default.tsx` | Fallback for parallel routes | |
| 40 | |
| 41 | ## Route Segments |
| 42 | |
| 43 | ``` |
| 44 | app/ |
| 45 | ├── blog/ # Static segment: /blog |
| 46 | ├── [slug]/ # Dynamic segment: /:slug |
| 47 | ├── [...slug]/ # Catch-all: /a/b/c |
| 48 | ├── [[...slug]]/ # Optional catch-all: / or /a/b/c |
| 49 | └── (marketing)/ # Route group (ignored in URL) |
| 50 | ``` |
| 51 | |
| 52 | ## Parallel Routes |
| 53 | |
| 54 | ``` |
| 55 | app/ |
| 56 | ├── @analytics/ |
| 57 | │ └── page.tsx |
| 58 | ├── @sidebar/ |
| 59 | │ └── page.tsx |
| 60 | └── layout.tsx # Receives { analytics, sidebar } as props |
| 61 | ``` |
| 62 | |
| 63 | ## Intercepting Routes |
| 64 | |
| 65 | ``` |
| 66 | app/ |
| 67 | ├── feed/ |
| 68 | │ └── page.tsx |
| 69 | ├── @modal/ |
| 70 | │ └── (.)photo/[id]/ # Intercepts /photo/[id] from /feed |
| 71 | │ └── page.tsx |
| 72 | └── photo/[id]/ |
| 73 | └── page.tsx |
| 74 | ``` |
| 75 | |
| 76 | Conventions: |
| 77 | |
| 78 | - `(.)` - same level |
| 79 | - `(..)` - one level up |
| 80 | - `(..)(..)` - two levels up |
| 81 | - `(...)` - from root |
| 82 | |
| 83 | ## Private Folders |
| 84 | |
| 85 | ``` |
| 86 | app/ |
| 87 | ├── _components/ # Private folder (not a route) |
| 88 | │ └── Button.tsx |
| 89 | └── page.tsx |
| 90 | ``` |
| 91 | |
| 92 | Prefix with `_` to exclude from routing. |
| 93 | |
| 94 | ## Middleware / Proxy |
| 95 | |
| 96 | ### Next.js 14-15: ` |
| 97 | |
| 98 | ```ts |
| 99 | // middleware.ts (root of project) |
| 100 | import { NextResponse } |
| 101 | import type { NextRequest |
| 102 | |
| 103 | export function middleware(request |
| 104 | // Auth, redirects, rewrites, etc. |
| 105 | return NextResponse.next |
| 106 | } |
| 107 | |
| 108 | export const config = |
| 109 | matcher: ["/dashboard/:path*", |
| 110 | }; |
| 111 | ``` |
| 112 | |
| 113 | ### Next.js 16+: ` |
| 114 | |
| 115 | Renamed for clarity - same capabilities, different names: |
| 116 | |
| 117 | ```ts |
| 118 | // proxy.ts (root of project) |
| 119 | import { NextResponse } |
| 120 | import type { NextRequest |
| 121 | |
| 122 | export function proxy(request |
| 123 | // Same logic as middleware |
| 124 | return NextResponse.next |
| 125 | } |
| 126 | |
| 127 | export const config = |
| 128 | matcher: ["/dashboard/:path*", |
| 129 | }; |
| 130 | ``` |
| 131 | |
| 132 | | Version | File | Export | Config | |
| 133 | | ------- | --------------- | |
| 134 | | v14-15 | `middleware.ts` | |
| 135 | | v16+ | `proxy.ts` | |
| 136 | |
| 137 | **Migration**: Run `npx @next/codemod@latest upgrade |
| 138 | |
| 139 | ## File Conventions Reference |
| 140 | |
| 141 | Reference: https://nextjs.org/docs/app/api-reference/file-conventions |