> ## 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.

# Add pages

> React pages with client-side routing, authentication, and pre-built system pages

The frontend is a React SPA with client-side routing, built-in authentication, and pre-built system pages for admin, settings, monitoring, and docs. You write page components and route definitions — the framework handles everything else.

## App entry point

Every app extends `BaseBrowserApp`. For the task tracker, register routes for the dashboard, task list, and task detail pages:

```typescript theme={null}
// src/web/App.tsx
import "@synthetiq/app-framework/web/styles";
import "./index.css";
import {
  BaseBrowserApp, configureFramework, initTrpc,
  systemRoutes, ProtectedRoute,
} from "@synthetiq/app-framework/web";
import { APP_ID, BASE_PATH, API_BASE } from "../constants";
import { DashboardPage } from "./pages/Dashboard";
import { TasksPage } from "./pages/Tasks";
import { TaskDetailPage } from "./pages/TaskDetail";

configureFramework({ basePath: BASE_PATH, appId: APP_ID, apiBase: API_BASE });
initTrpc();

class App extends BaseBrowserApp {
  constructor() {
    super({ basePath: BASE_PATH, displayName: "Task Tracker" });

    this.useRouter({
      ...systemRoutes(BASE_PATH, "Task Tracker"),
      "/": {
        title: "Dashboard",
        description: "Overview of tasks and recent activity.",
        metaTitle: "Dashboard | Task Tracker",
        metaDescription: "View your task dashboard",
        render: () => (
          <ProtectedRoute>
            <DashboardPage />
          </ProtectedRoute>
        ),
      },
      "/tasks": {
        title: "Tasks",
        description: "List of all tasks with filtering by status and priority.",
        metaTitle: "Tasks | Task Tracker",
        metaDescription: "View and manage your tasks",
        render: () => (
          <ProtectedRoute>
            <TasksPage />
          </ProtectedRoute>
        ),
      },
      "/tasks/:id": {
        title: "Task Detail",
        description: "Detailed view of a single task with edit and status actions.",
        metaTitle: "Task | Task Tracker",
        metaDescription: "View task details",
        render: (routeParams) => (
          <ProtectedRoute>
            <TaskDetailPage id={routeParams?.id ?? ""} />
          </ProtectedRoute>
        ),
      },
    });
  }
}

new App();
```

Routes wrapped in `ProtectedRoute` require authentication — unauthenticated users are redirected to `/login`. The `title` and `description` fields are used by the build pipeline to generate `pages.json`, which gives the AI agent awareness of the app's page structure.

## Page components

The routes above reference `TasksPage` and `TaskDetailPage` — these are page components that live in `src/web/pages/`. Create the tasks list page:

```typescript theme={null}
// src/web/pages/Tasks.tsx
import { useAuth } from '@synthetiq/app-framework/web';
import { trpcReact } from '../trpc';

export function TasksPage() {
  const { scopes } = useAuth();
  const isAdmin = scopes.includes('tasks:viewAll');

  const { data: tasks, isLoading } = isAdmin
    ? trpcReact.tasks.getAllTasks.useQuery()
    : trpcReact.tasks.getMyTasks.useQuery();

  if (isLoading) return <div>Loading...</div>;

  return (
    <div>
      <h1>Tasks</h1>
      {tasks?.map(task => (
        <a key={task.id} href={`/tasks/${task.id}`}>
          {task.title} — {task.status}
        </a>
      ))}
    </div>
  );
}
```

And the task detail page:

```typescript theme={null}
// src/web/pages/TaskDetail.tsx
import { useAuth } from '@synthetiq/app-framework/web';
import { trpcReact } from '../trpc';

export function TaskDetailPage({ id }: { id: string }) {
  const { scopes } = useAuth();
  const isAdmin = scopes.includes('tasks:viewAll');

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

  const utils = trpcReact.useUtils();
  const task = tasks?.find(t => t.id === id);

  const updateMutation = trpcReact.tasks.updateTask.useMutation({
    onSuccess: () => {
      utils.tasks.getMyTasks.invalidate();
      utils.tasks.getAllTasks.invalidate();
    },
  });

  if (!task) return <div>Task not found</div>;

  return (
    <div>
      <h1>{task.title}</h1>
      <p>Status: {task.status}</p>
      <p>Priority: {task.priority}</p>
      <button onClick={() => updateMutation.mutate({ id, status: 'completed' })}>
        Mark Complete
      </button>
    </div>
  );
}
```

Page components live in `src/web/pages/` and correspond to routes registered in `App.tsx`. Reusable UI elements shared across pages (headers, cards, layouts) go in `src/web/components/` instead.

## Authentication

Use the `useAuth` hook to access user info and scopes:

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

function Header() {
  const { user } = useAuth();
  return <span>Welcome, {user?.name || user?.email}</span>;
}
```

## UserMenu

Include the `UserMenu` component in your header. It provides the user avatar, organization switcher, admin links, and sign out:

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

function Header() {
  return (
    <header className="flex h-14 items-center justify-between border-b border-border px-4">
      <span className="font-medium">Task Tracker</span>
      <UserMenu />
    </header>
  );
}
```

## System pages

`systemRoutes()` provides pre-built pages for login, admin, settings, monitoring, and docs — no app code required. For the full list, see the [Routing reference](/docs/platform-docs/app-framework/reference/routing).

For RouteConfig properties, dynamic routes, programmatic navigation, and scope-based route protection, see the [Routing reference](/docs/platform-docs/app-framework/reference/routing).
