> ## Documentation Index
> Fetch the complete documentation index at: https://synthetiq.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Routing reference

> RouteConfig properties, system routes, navigation, and route protection

## RouteConfig

| Property          | Type                                           | Description                                          |
| ----------------- | ---------------------------------------------- | ---------------------------------------------------- |
| `title`           | `string`                                       | Short page title (used in docs and AI agent context) |
| `description`     | `string`                                       | What the page contains and what users can do on it   |
| `metaTitle`       | `string`                                       | Browser tab title                                    |
| `metaDescription` | `string`                                       | Meta description tag                                 |
| `render`          | `(routeParams?, searchParams?) => JSX.Element` | Render function                                      |

## Route protection

```typescript theme={null}
// Authentication required
<ProtectedRoute>
  <MyPage />
</ProtectedRoute>

// Scope-based (user must have at least one of the listed scopes)
<ProtectedRoute requiredScopes={["users:viewAll", "roles:view"]}>
  <AdminPage />
</ProtectedRoute>
```

Frontend route protection is a UX convenience — the actual security enforcement happens on the backend via `scopedProcedure` and database-level RLS.

## Dynamic routes

```typescript theme={null}
"/tasks/:id": {
  render: (routeParams) => <TaskPage id={routeParams?.id ?? ""} />,
},

"/projects/:owner/:repo": {
  render: (routeParams, searchParams) => (
    <ProjectPage owner={routeParams?.owner ?? ""} repo={routeParams?.repo ?? ""} />
  ),
},
```

## System routes

`systemRoutes(basePath, displayName)` provides:

| Route         | Page                                                                 | Protection    |
| ------------- | -------------------------------------------------------------------- | ------------- |
| `/login`      | Login page                                                           | Public        |
| `/admin/*`    | Admin pages (users, roles, orgs, scopes, services, monitoring, logs) | Admin scopes  |
| `/settings/*` | User settings (OAuth apps)                                           | Authenticated |
| `/docs/*`     | API docs, data models, workflows                                     | Authenticated |

Do not redefine `/login` when using `systemRoutes()`.

## Navigation

Standard `<a>` tags with relative paths are automatically intercepted for client-side navigation:

```html theme={null}
<a href="/tasks">Tasks</a>
<a href="/tasks/123">Task 123</a>
```

### Programmatic navigation

```typescript theme={null}
import { useApp } from "@synthetiq/app-framework/web";

function MyComponent() {
  const { basePath } = useApp();

  const navigateTo = (path: string) => {
    window.history.pushState({}, "", basePath.concat(path));
    window.dispatchEvent(new PopStateEvent("popstate"));
  };

  return <button onClick={() => navigateTo("/settings")}>Settings</button>;
}
```

### Active navigation state

```typescript theme={null}
import { useApp } from "@synthetiq/app-framework/web";

function Layout({ children }: { children: React.ReactNode }) {
  const { basePath } = useApp();
  const currentPath = window.location.pathname.replace(basePath, '') || '/';

  return (
    <div className="flex">
      <nav>
        <a href="/" className={currentPath === '/' ? 'bg-background-secondary' : ''}>Dashboard</a>
        <a href="/tasks" className={currentPath.startsWith('/tasks') ? 'bg-background-secondary' : ''}>Tasks</a>
      </nav>
      <main>{children}</main>
    </div>
  );
}
```

## useAuth

```typescript theme={null}
import { useAuth } from "@synthetiq/app-framework/web";

const {
  user,            // SynthetiqUser | null
  scopes,          // string[] — user's permission scopes
  loading,         // boolean — auth initialization in progress
  isAuthenticated, // boolean
  hasAppAccess,    // boolean — whether user can access this app
  currentOrgId,    // string | null — selected organization
  organizations,   // { id: string; name: string }[] — user's orgs
  setCurrentOrgId, // (orgId: string) => void — used by built-in UserMenu org switcher
  login,           // () => Promise<void>
  logout,          // () => void
} = useAuth();
```

### User properties

| Property               | Type                   | Description               |
| ---------------------- | ---------------------- | ------------------------- |
| `user.id` / `user.sub` | `string`               | User ID (same value)      |
| `user.email`           | `string \| undefined`  | Email address             |
| `user.email_verified`  | `boolean \| undefined` | Email verification status |
| `user.name`            | `string \| undefined`  | Display name              |
| `user.picture`         | `string \| undefined`  | Profile picture URL       |

## Conditional rendering with scopes

```typescript theme={null}
const { scopes } = useAuth();
const hasViewAll = scopes.includes('tasks:viewAll');

const { data } = hasViewAll
  ? trpcReact.tasks.getAllTasks.useQuery()
  : trpcReact.tasks.getMyTasks.useQuery();
```
