wcz-layout
Version:
124 lines (112 loc) • 4.35 kB
Markdown
name: routing
description: "Use when: creating or modifying TanStack Router routes, feature folders, navigation, permissions, loaders, suspense flows, or route-scoped components/hooks."
## Rules
- Always include `requirePermission("all")` in the route's `beforeLoad` function to enforce access control unless the route is meant to be public.
- 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 root-level collections.
- 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 `@mui/icons-material` in `src/routes/__root.tsx`.
## Base Project Structure
```txt
src/ # client-first architecture
├── components/ # shared components across multiple routes
├── db-collections/ # tanstack-db collections
├── hooks/ # shared hooks across multiple routes
├── lib/ # contains isomorphic/shared logic usable by both client and server
│ ├── auth/
│ │ ├── permissions.ts
│ │ └── scopes.ts
│ ├── locales/
│ │ ├── cs.json
│ │ └── en.json
│ └── schemas/ # shared zod schemas
├── routes/ # TanStack Router file-based routing
│ ├── __root.tsx
│ ├── index.tsx
│ └── features/ # route group with multiple pages
│ ├── -components/ # components specific to feature routes
│ ├── -hooks/ # hooks specific to feature routes
│ ├── index.tsx
│ ├── create.tsx
│ ├── edit.$id.tsx
│ └── $id.tsx
├── server/ # server-only code
│ ├── db/
│ │ ├── migrations/
│ │ ├── schemas/ # drizzle database schemas
│ │ │ └── feature.ts
│ │ └── index.ts # drizzle db instance
│ ├── middleware/
│ │ └── databaseMiddleware.ts
│ └── feature.ts # server functions / api logic
└── types/
```
## 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("all"),
pendingComponent: FeatureSkeleton,
loader: () => {
featureCollection.preload();
},
});
function RouteComponent() {
const { t } = useTranslation();
const { alert, confirm } = useDialogs();
const { user } = Route.useRouteContext();
// route component code...
}
// __root.tsx
const { user } = Route.useRouteContext();
const navigation: Navigation = [
{
kind: "item",
to: "/",
title: "Home",
icon: <Home />,
hidden: !user?.hasPermission("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 />,
},
],
},
];
// __root.tsx how to add public route
<LayoutProvider {/* other props */} options={{
publicRoutes: ["/feature-name"]
}}>
```