# The web app is organized by feature, like the backend

apps/web/src moves to routes, features and platform with fixed role names, server-only modules carry a .server suffix, and the rules are checked by a boundary test.

**Breaking for forks that changed `apps/web/src`.** The folders `components/`, `hooks/`, `lib/`, `utils/`, `functions/`, `middleware/`, `og/` and `data/` are gone. Code now lives in `apps/web/src/features/<feature>/<feature>.<role>.ts(x)`, with the same feature names as the backend where one exists (`todo`, `documents`, `billing`, …), and in `apps/web/src/platform/<area>/` for what has no domain (the oRPC client, session, i18n, errors, SEO, OG). Routes stay where they are and get thinner. No URL, route or translation key changes, and every route answers as before. The only visible copy change is the web code sample on the landing page (and in `llms-full.txt`), which now names `apps/web/src/features/landing/landing.hooks.ts`. If your fork edited a moved file, re-apply the change at its new path; `git log --follow` finds every file.

- **Fixed roles.** A feature has `*.queries.ts` (query options and the loader's prefetch), `*.hooks.ts` (the logic), `*.page.tsx`, presentational components, pure model files and, for server-only code, `*.server.ts(x)`. Components no longer import TanStack Query, the oRPC client or the auth client directly. [Web conventions](/docs/development/web-conventions), [ADR-0012](/docs/adr/0012-web-features-mirror-the-backend-shape).
- **Server-only code is named `.server`.** TanStack Start refuses `*.server.*` modules in the client build, so the name is checked by the framework. Client code reaches server code only through two isomorphic bridges (the session query and the docs page loader), and the web keeps zero `createServerFn`. [Client/server boundary](/docs/architecture/client-server-boundary).
- **Rules with checks.** A boundary test (`scripts/web-feature-boundaries.test.mjs`) checks layers, cross-feature imports, the server suffix, barrels, `.client.` names, dynamic imports and stale `vi.mock` or worker URLs. The same test checks import cycles, and file size is a lint warning. The [enforcement matrix](/docs/development/enforcement) lists W1–W13.
- **Locales stay central.** `apps/web/src/locales/{en,de,fr}.ts` keep every key; new keys use their feature's prefix.
- **A skill section for new screens.** The `domain-feature` skill gains a web section with templates for queries, hooks, the page and a thin route.

Known leftover: a comment in `apps/mobile/src/lib/user-agent.ts` still names the old web path; it changes with the next mobile change. Unused vendored components moved as they were; removing them is a separate decision.

Measurements ([report](https://github.com/CarlosZiegler/template-cloudflare/blob/main/docs/measurements/2026-10-17-web-structure.md)): the client grows by 425 B gzip in total, and its entry chunk shrinks by 1,128 B because loaders import `*.queries` instead of the hooks. The server code grows by 850 B gzip, and no Effect module reaches the client. The docs pages added or edited with this change (ADR-0012, the web conventions, this entry and the updated guides) add about 23.6 KiB gzip to the server's docs chunk, so the server grows by about 24.5 KiB gzip in total; the docs do not reach the client.

Sources: [platform moves](https://github.com/CarlosZiegler/template-cloudflare/commit/e6a9ff6), [platform, second part](https://github.com/CarlosZiegler/template-cloudflare/commit/3356c49), [OG with server suffixes](https://github.com/CarlosZiegler/template-cloudflare/commit/0e2d979), [feature folders, first of 23 moves](https://github.com/CarlosZiegler/template-cloudflare/commit/d18d2e9), [last feature move](https://github.com/CarlosZiegler/template-cloudflare/commit/0a1934b), [boundary test](https://github.com/CarlosZiegler/template-cloudflare/commit/6ce11f4), [boundary test in the root tests](https://github.com/CarlosZiegler/template-cloudflare/commit/1d085c9), [file size warning](https://github.com/CarlosZiegler/template-cloudflare/commit/236d010), [bundle markers for web server modules](https://github.com/CarlosZiegler/template-cloudflare/commit/63c00d0), [thin todos route](https://github.com/CarlosZiegler/template-cloudflare/commit/41a87fc), [files gateway](https://github.com/CarlosZiegler/template-cloudflare/commit/60d1316), [root document](https://github.com/CarlosZiegler/template-cloudflare/commit/0bcdf98), [loaders out of routes](https://github.com/CarlosZiegler/template-cloudflare/commit/bcd2eb7), [measurement](https://github.com/CarlosZiegler/template-cloudflare/commit/4670ccc).

