---
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"]
}}>
```
