---
title: TanStack Start
description: The app framework conventions - file routing, server functions, server-first rendering, and the web app's route tree.
---

Africa OS apps are built on **TanStack Start** (not Next.js). This page covers the conventions the repository follows: file-based routing, server functions, server-first rendering, and the shared app patterns.

### File-based routing

TanStack Start uses file-based routing, like TanStack Router. Files under `src/routes/` become routes:

```txt
routes/
├── __root.tsx                 root layout (html, providers)
├── _authenticated.tsx         layout group with an auth guard
├── _authenticated/index.tsx   the launcher at "/"
├── _authenticated/$organization.tsx  layout for "/:organization"
└── _authenticated/$organization/$application.tsx  mounts an app
```

Conventions used in this repo:

- **Layout groups** are folders or files with a pathless prefix (`_authenticated`) - the segment is not part of the URL but its layout applies to nested routes.
- **Dynamic segments** use `$` (`$organization`, `$application`).
- **Guards live in `beforeLoad`** - the route's data-loading hook. See [Web app](/frontend/web-app).

The route tree is code-generated with `tsr generate` (`pnpm generate-routes` in each app), and kept in sync by the dev server.

### Server functions

Server functions are the RPC layer - the TanStack Start replacement for a REST API. They run server-side and are callable from the client with full types:

```ts
import { createServerFn } from "@tanstack/react-start";
import { getRequestHeaders } from "@tanstack/react-start/server";

export const listProductsFn = createServerFn({ method: "GET" })
  .validator((data: unknown) => schema.parse(data))
  .handler(async ({ data }) => {
    const headers = getRequestHeaders();
    // ... run an Effect program through the request runtime
  });
```

The rules every server function follows:

1. **Validate input** with a Zod validator before anything else.
2. **Read headers via `getRequestHeaders()`** and pass them to the request runtime.
3. **Run the Effect program through `runRequest`/`runRequestFolded`** - never hand-assemble session, org, or database.
4. **Fold typed failures into serializable results** - client code never sees thrown errors.
5. **Scope to the organization** via the request scope (URL slug) whenever the call targets tenant data.

### Server-first rendering

The repository prefers **server components and server functions first**. Data fetching happens server-side; client components are added only when explicitly needed (forms, sheets, live updates). This keeps business logic and tenancy enforcement server-side and the client bundle lean.

### The shared app pattern

Every TanStack Start app in the repo follows the same skeleton:

```txt
src/
├── router.tsx                    createRouter + route tree import
├── routes/                       file-based routes
├── components/                   app-level components
└── features/                     domain features (auth, applications, ...)
```

App-specific features live under `src/features/<name>/` with `services/` for server functions, `hooks/` for Query hooks, and `components/` for UI - the same feature structure the application packages use.

### The web app's guard

Authentication is enforced in the `_authenticated` group's `beforeLoad`:

```ts
export const Route = createFileRoute("/_authenticated")({
  beforeLoad: async () => {
    // session check via a server function; redirect to /login when missing
  },
});
```

Route loaders that need data call server functions, which run through the request runtime and therefore carry the full request context.

### Next steps

- [Data fetching](/frontend/data-fetching) - how server functions and Query compose.
- [Web app](/frontend/web-app) - the full route tree in practice.