L'idée clé
Avec Next.js App Router, la structure des dossiers est ta hiérarchie d'URL. Tu ne déclares plus de routes dans un fichier de config (comme urls.py en Django) : Next lit l'arborescence sous app/ (ou src/app/) et chaque dossier devient un segment d'URL.
app/
├── page.tsx → GET /
├── admin/
│ ├── page.tsx → GET /admin
│ └── produits/
│ └── page.tsx → GET /admin/produits
└── api/
└── panier/
└── route.ts → /api/panier (handlers HTTP : GET, POST, etc.)Les fichiers à conventions strictes
Chaque dossier peut contenir des fichiers avec un nom réservé qui jouent un rôle précis :
| Fichier | Rôle |
|---|---|
page.tsx | La page elle-même (rendue à cette URL) |
layout.tsx | Coquille englobante (header, sidebar, etc.) — voir l'article dédié |
loading.tsx | UI affichée pendant que page.tsx charge ses données |
error.tsx | UI affichée si page.tsx plante |
not-found.tsx | UI affichée si notFound() est appelé |
route.ts | Handler API (pas une page) — exporte GET, POST, etc. |
Routes dynamiques : [slug]
Un dossier entre crochets capture la valeur dans l'URL :
app/products/[id]/page.tsx → /products/42, /products/abc, etc.Dans la page, tu reçois params.id (en Next 15+, c'est un Promise à await) :
export default async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
// ...
}Pourquoi c'est différent de Django / Flask / Express
Tu as l'habitude (côté Python) d'écrire un fichier urls.py qui mappe /products/<id> à une vue. En Next, le mapping vient gratuitement de l'arborescence — pas de table de routes à maintenir. Le revers : tu ne peux pas "voir" toutes les routes d'un coup, faut explorer les dossiers.
Piège classique
Pages vs Route handlers sont distincts :
page.tsxrend du HTML (UI) — accessible via navigateurroute.tsrend du JSON / autre (API) — accessible viafetch()ou curl
Si tu mets les deux dans le même dossier, Next refuse de builder. Sépare : pages sous app/..., APIs sous app/api/....