Skip to content

Routing (TanStack Router)

Routes are files under src/routes/, regenerated by npm run generate-routes after every add/remove.

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)
core/auth/guards.ts
requireAuth(session) // → redirect('/login') when not logged in
requirePermissions(session, 'FINANCES.VOIR', ...) // → redirect('/') when denied
routes/_authenticated.tsx
beforeLoad: ({ 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' }).

  1. 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.
  2. Per-route data loads (loader) are reserved for cases where the component needs data before first render. For everything else: TanStack Query (State management).
  3. routeTree.gen.ts is generated, never edited.
  • ❌ 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) — use useRouter() or <Link>.