wcz-layout
Version:
95 lines (83 loc) • 3.67 kB
Markdown
---
name: routing
description: "Use when: creating or modifying TanStack Router routes, navigation, permissions, loaders, etc."
---
> Mechanics (`createFileRoute`, `beforeLoad`, `redirect`, `loader`, preloading, `pendingComponent`) are covered by TanStack's own skills — load `/router-core#router-core/auth-and-guards`, `#router-core/data-loading`, and `#router-core/navigation` for the API details. This skill adds only the wcz-layout conventions on top.
## Rules
- Include the `requirePermission(<permissionKey>)` function in the route's `beforeLoad` to enforce access control — pass a key defined in your app's `src/lib/auth/permissions.ts`; omit it only for intentionally public routes.
- Use `Route.useRouteContext()` to access the authenticated user. User can be `null` if the route doesn't include `requirePermission`.
- Use the route `loader` option to preload db collections. Preload only collections with `eager` syncMode.
- Create a `pendingComponent` for routes that suspend or preload meaningful data.
- Route names must always be kebab-case. Entity route names must always be plural.
- If a feature contains only one route, create a single file route: `src/routes/libraries.tsx`
- If a feature contains multiple related routes, create a folder: `src/routes/libraries/index.tsx`
- Colocate route-specific components/hooks inside `routes/<feature>/-components` or `-hooks`.
- Root-level `components/` and `hooks/` are only for code shared across multiple routes.
- After creating a new route, add or ask for a navigation item with a unique icon from `/icons-material` in `src/routes/__root.tsx`.
- For navigation and redirects, always use the type-safe router components from `wcz-layout/components` (`RouterButton`, `RouterIconButton`, `RouterLink`, `RouterFab`, `RouterTab`, `RouterListItemButton`, `RouterGridActionsCellItem`) rather than raw MUI with a manual `href` — they wrap `createLink`, so `to`/`params`/`search` are type-checked.
## Examples
```ts
// imports
import { useDialogs, useTranslation } from "wcz-layout/hooks";
import { requirePermission, uuidv7 } from "wcz-layout/utils";
// src/routes/features/create.tsx
export const Route = createFileRoute("/features/create")({
component: RouteComponent,
beforeLoad: requirePermission("admin"),
pendingComponent: FeatureSkeleton,
loader: () => {
featureCollection.preload();
},
});
function RouteComponent() {
const { t } = useTranslation();
const { alert, confirm } = useDialogs();
const { user } = Route.useRouteContext();
// route component code...
}
// __root.tsx
import { hasPermission } from "wcz-layout/utils";
const { user } = Route.useRouteContext();
const navigation: Navigation = [
{
kind: "item",
to: "/",
title: "Home",
icon: <Home />,
hidden: !hasPermission(user, "all"),
},
{
kind: "header",
title: "Documentation",
},
{
kind: "group",
title: "Components",
icon: <Widgets />,
children: [
{
kind: "item",
to: "/components/navigation",
title: "Navigation",
icon: <AccountTree />,
},
{
kind: "divider",
},
{
kind: "item",
to: "/components/forms",
title: "Forms",
icon: <Code />,
},
],
},
];
// Routes are public unless guarded. By convention, protect every route by adding
// requirePermission to its beforeLoad (omit only for intentionally public routes) —
// it redirects to login when signed out, then checks the permission group.
export const Route = createFileRoute("/feature-name")({
component: RouteComponent,
beforeLoad: requirePermission("admin"),
});
```