Web
The TanStack Start front end — file-based routing, server functions, and the shared UI library, on React 19 + Vite.
apps/web is the primary user-facing front end: a TanStack Start application built on TanStack Router, Vite, and React 19. It serves on port 3001. This is deliberately not a Next.js app — there is no app/ directory and no React Server Components, so patterns you might reach for from Next.js do not apply here.
Stack
The app is assembled from a small, focused set of tools:
- TanStack Start (
@tanstack/react-start) plus TanStack Router (@tanstack/react-router) for the framework and routing. - Vite as the build tool, configured with
@tanstack/router-plugin,@tailwindcss/vite, and@vitejs/plugin-react. - React 19, Tailwind CSS v4, and shadcn/ui delivered through the shared
@halostack/uipackage.
Routing
Routing is file-based. Every file under src/routes/ maps directly to a URL — the file path is the route. The table below shows the conventions you will encounter most often:
Two files anchor the router itself. src/routeTree.gen.ts is generated by the router plugin and regenerates on every dev and build run — never edit it by hand. src/router.tsx builds the router via getRouter() and registers the route types through a declare module "@tanstack/react-router" block.
Adding a route
To add a page, create a file under src/routes/ and export a Route built with createFileRoute. The router plugin picks it up automatically:
A few conventions keep route code consistent:
- Data loading: fetch in the route
loaderand read it back withRoute.useLoaderData(), rather than fetching from auseEffect. - Server logic: wrap server-only code in
createServerFn()from@tanstack/react-start. There are no instances of this yet — follow this pattern for the first one. - Navigation: move between routes with
LinkoruseNavigate()from@tanstack/react-router, both typed against the generated route tree.
Forms
Build forms with the app-local form kit — useAppForm from @/lib/form/use-app-form, composed with field.TextField — rather than a bare form element wired up with useState. Mark mandatory fields with required (which renders the * marker and sets aria-required), set autoComplete and type where they apply, validate with zod through validators, and surface inline errors through FieldError with match={true}. The toast helpers require a description, so every notification carries supporting copy.
The full rules live in .agents/rules/forms.md and .agents/rules/interactions.md. For working examples, see src/components/todos/todo-create-form.tsx, src/components/account/security-section.tsx, and the forms under src/routes/auth/*.
Imports and styling
Two path aliases keep imports clean and are resolved by Vite's tsconfigPaths:
@/*points at app-local files underapps/web/src/*.@halostack/ui/*points at the shared UI library.
Styling is Tailwind v4 together with the cn helper from @halostack/ui/lib/utils. Global styles come from @halostack/ui/globals.css, imported once in __root.tsx.