UNPKG

wcz-layout

Version:

96 lines (87 loc) • 5.09 kB
--- name: routing description: "Use when adding or changing a page, route file, navigation entry, search params, or route-level access control. Covers createFileRoute, beforeLoad requireAuth, loader preloads, validateSearch, useSearch, src/routes/ naming and colocation, __root.tsx navigation and LayoutProvider options (app bar actions, showShell), the auth and login routes, and the RouterLink/RouterButton link components." metadata: type: convention library: wcz-layout --- > Mechanics (route definitions, loaders, context, guards, navigation, search params, > route-tree fundamentals) are covered by TanStack's own skills. List the skills shipped by > `@tanstack/router-core` and `@tanstack/react-router` and load whichever cover the task at > hand. This skill adds the wcz-layout conventions. ## Rules - Include `requireAuth(<permissionKey>)` in the route's `beforeLoad` to enforce access control — pass a key defined in your app's `src/lib/auth/permissions.ts`. Use bare `requireAuth()` when any signed-in user may enter, and omit it entirely only for intentionally public routes. - Route-level `requireAuth` only hides the UI. Every server function or server route that accesses protected data still needs `authMiddleware`; see the `server-functions` skill. - Use `Route.useRouteContext()` to access the authenticated user. `user` can be `null` if the route doesn't include `requireAuth`. - Preload the route's live queries in the loader and return the promise: `loader: ({ context }) => context.dbClient.preloadLiveQuery(<x>QueryOptions)`. Detail routes await it and throw `notFound()` for a missing row; see the `client-db` skill. - Literal URL segments and feature folders use kebab-case. Entity feature names are plural; TanStack special route tokens retain their required syntax. - If a feature contains only one route, create a single file route: `src/routes/<feature>s.tsx` - If a feature contains multiple related routes, create a folder: `src/routes/<feature>s/index.tsx` - Colocate route-specific components/hooks inside `routes/<feature>s/-components` or `-hooks`. - When a route needs its own components/hooks that no sibling route uses, wrap it in a route group folder named after the route and colocate them there: `routes/<feature>s/(index)/index.tsx` with `(index)/-components` and `(index)/-hooks`. - Root-level `components/` and `hooks/` are only for code shared across multiple routes. - After creating a new route, add a navigation item with a unique icon from `@mui/icons-material` in `src/routes/__root.tsx`. - `Navigation` kinds: `item` (`title`, `icon`, `to` + `params`/`search`, or `href` for an external URL), `group` (`title`, `icon`, `children`), `header` (`title`) and `divider`. - Hide a navigation entry from users who cannot open it: `hidden: !hasPermission(user, <key>)` with the same key as its route's `requireAuth(<key>)`. - `LayoutProvider` in `__root.tsx` takes `theme`, `navigation` and `options`: `appBarActions` (nodes between the app title and the account menu) and `showShell` (app bar + navigation visibility). - For user-clickable in-app navigation, use the type-safe router components from `wcz-layout/components` (`RouterButton`, `RouterIconButton`, `RouterLink`, `RouterFab`, `RouterTab`, `RouterListItemButton`) rather than raw MUI with a manual `href` or an `onClick` with `navigate()`. They wrap `createLink`, so `to`/`params`/`search` are type-checked and the router's `defaultPreload: "intent"` preloads the target's loader on hover/focus. They accept the matching MUI props. Keep `useNavigate()` for navigation after an action (e.g. after a successful save). ## Examples ```tsx // imports import AccountTree from "@mui/icons-material/AccountTree"; import Code from "@mui/icons-material/Code"; import Home from "@mui/icons-material/Home"; import Widgets from "@mui/icons-material/Widgets"; import { createFileRoute } from "@tanstack/react-router"; import type { Navigation } from "wcz-layout"; import { useDialogs, useTranslation } from "wcz-layout/hooks"; import { hasPermission, requireAuth } from "wcz-layout/utils"; // src/routes/<feature>s/index.tsx export const Route = createFileRoute("/<feature>s/")({ component: RouteComponent, beforeLoad: requireAuth("admin"), loader: ({ context }) => context.dbClient.preloadLiveQuery(featuresQueryOptions), }); function RouteComponent() { const { t } = useTranslation(); const { alert, confirm } = useDialogs(); const { user } = Route.useRouteContext(); // route component code... } // __root.tsx navigation const navigation: Navigation = [ { kind: "item", to: "/", title: t("Home"), icon: <Home /> }, { kind: "header", title: t("Documentation") }, { kind: "group", title: t("Components"), icon: <Widgets />, hidden: !hasPermission(user, "admin"), children: [ { kind: "item", to: "/components/navigation", title: t("Navigation"), icon: <AccountTree /> }, { kind: "divider" }, { kind: "item", to: "/components/forms", title: t("Forms"), icon: <Code /> }, ], }, ]; ```