Routing (TanStack Router)
Routes are files under src/routes/, regenerated by
npm run generate-routes after every add/remove.
Current structure
Section titled “Current structure”| File | Route | Role |
|---|---|---|
src/routes/__root.tsx |
— | HTML shell, locale, default error/404, devtools in dev |
src/routes/login.tsx |
/login |
Public; redirects to / when already logged in |
src/routes/mot-de-passe-oublie.tsx |
/mot-de-passe-oublie |
Public; requests the reset email |
src/routes/reinitialiser-mot-de-passe.tsx |
/reinitialiser-mot-de-passe |
Public; sets the new password (?token=) |
src/routes/_authenticated.tsx |
/_authenticated layout |
Protected; mounts AuthProvider + AppShell — staff back-office and resident portal (residence/portail/*) |
src/routes/_authenticated/index.tsx |
/ |
Protected home |
src/routes/_espace-client.tsx |
/espace-client layout |
Protected; mounts ClientShell (lg+ sidebar, navbar + mobile tab bar) |
Guards (beforeLoad)
Section titled “Guards (beforeLoad)”requireAuth(session) // → redirect('/login') when not logged inrequirePermissions(session, 'FINANCES.VOIR', ...) // → redirect('/') when deniedbeforeLoad: ({ context }) => { requireAuth(context.auth)}The route context exposes { queryClient, auth } (see router.tsx). Guards
throw redirect({ href: ... }) — an intentionally untyped string path so it
does not depend on the route tree. AuthProvider is mounted in the protected
layout only: the public login page accesses the session via
useRouteContext({ from: '/login' }).
- Guards run in
beforeLoad(executed before render, on the client and SSR). Under SSR, an unauthenticated user is redirected — the expected behavior for a client-side JWT app. - Per-route data loads (
loader) are reserved for cases where the component needs data before first render. For everything else: TanStack Query (State management). routeTree.gen.tsis generated, never edited.
Don’ts
Section titled “Don’ts”- ❌ Write business logic in a route (go to
features/). - ❌ Attach permissions to rendering only: the frontend guard is a UX convenience, security stays in the backend (Authorization).
- ❌ Navigate with
window.location(would break the SPA) — useuseRouter()or<Link>.
