Files
agentic-dev-template/apps/web-tanstack/AGENTS.md

159 lines
4.8 KiB
Markdown

# apps/web-tanstack -- TanStack Start Reference App
## Purpose
TanStack Start reference application using TanStack Router with file-based routing. Demonstrates how to consume `@repo/api-client` for tRPC data fetching and `@repo/ui` for components. Like `apps/web-next`, this is a thin app -- business logic lives in `@repo/core`, UI components live in `@repo/ui`.
## Port: 3002
```bash
pnpm dev --filter @repo/web-tanstack # http://localhost:3002
```
## Key Files
| File | Purpose |
|---|---|
| `src/routes/__root.tsx` | Root layout -- creates the root route, wraps with `<ApiProvider>` and `<Outlet>` |
| `src/routes/index.tsx` | Home page route (`/`) |
## File-Based Routing
TanStack Router uses file-based routing where file paths in `src/routes/` map directly to URL paths:
| File | URL | Description |
|---|---|---|
| `src/routes/__root.tsx` | (all routes) | Root layout, wraps all child routes |
| `src/routes/index.tsx` | `/` | Home page |
| `src/routes/about.tsx` | `/about` | Static page |
| `src/routes/articles/index.tsx` | `/articles` | Article listing |
| `src/routes/articles/$id.tsx` | `/articles/:id` | Single article (dynamic param) |
### Naming conventions:
- `__root.tsx` -- special root layout file, always wraps all routes
- `index.tsx` -- index route for its directory (e.g., `/articles/index.tsx` matches `/articles`)
- `$paramName.tsx` -- dynamic route segment (e.g., `$id.tsx` captures `:id`)
- Nested folders create nested URL segments
## Provider Setup
The `<ApiProvider>` wraps the entire app in `__root.tsx`:
```tsx
// src/routes/__root.tsx
import { Outlet, createRootRoute } from "@tanstack/react-router";
import { ApiProvider } from "@repo/api-client";
export const Route = createRootRoute({
component: () => (
<ApiProvider trpcUrl="http://localhost:3000/api/trpc">
<Outlet />
</ApiProvider>
),
});
```
Note: The `trpcUrl` points to the Next.js app's tRPC endpoint at `http://localhost:3000/api/trpc`. In production, this should be configured via environment variables.
## Recipe: Adding a New Route with Data Fetching
This example adds an `/articles` route that lists published articles.
### Step 1: Create the route file
Create `src/routes/articles/index.tsx`:
```tsx
import { createFileRoute } from "@tanstack/react-router";
import { useTRPC } from "@repo/api-client";
import { useQuery } from "@tanstack/react-query";
import { Button } from "@repo/ui";
export const Route = createFileRoute("/articles/")({
component: ArticlesPage,
});
function ArticlesPage() {
const trpc = useTRPC();
const { data, isLoading, error } = useQuery(
trpc.content.listArticles.queryOptions({ status: "published", limit: 20 })
);
if (isLoading) return <p>Loading articles...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<main>
<h1>Articles</h1>
<ul>
{data?.map((article) => (
<li key={article.id}>
<h2>{article.title}</h2>
<Button variant="outline" size="sm">
Read more
</Button>
</li>
))}
</ul>
</main>
);
}
```
### Step 2: Add a dynamic route for individual articles
Create `src/routes/articles/$id.tsx`:
```tsx
import { createFileRoute } from "@tanstack/react-router";
import { useTRPC } from "@repo/api-client";
import { useQuery } from "@tanstack/react-query";
export const Route = createFileRoute("/articles/$id")({
component: ArticlePage,
});
function ArticlePage() {
const { id } = Route.useParams();
const trpc = useTRPC();
// Use the article ID from the URL parameter
// (Assuming a getArticle procedure exists on the content router)
const { data, isLoading } = useQuery(
trpc.content.listArticles.queryOptions({ limit: 1 })
);
if (isLoading) return <p>Loading...</p>;
return (
<main>
<h1>Article {id}</h1>
</main>
);
}
```
Key patterns:
- Every route file exports a `Route` created via `createFileRoute(path)(...)`
- The `component` property defines the React component for that route
- Use `Route.useParams()` to access dynamic parameters (e.g., `$id`)
- Data fetching uses the same `useTRPC()` + `useQuery()` pattern as Next.js
- Import UI components from `@repo/ui`, never recreate them locally
## Dependencies
| Dependency | Purpose |
|---|---|
| `@repo/api` | `AppRouter` type (transitive via `@repo/api-client`) |
| `@repo/api-client` | `ApiProvider` + `useTRPC()` for client-side data fetching |
| `@repo/ui` | Shared UI components |
| `@tanstack/react-router` | TanStack Router for file-based routing |
| `react` / `react-dom` | React 19 runtime |
## Cross-References
- **tRPC routers:** `packages/api/` -- see `packages/api/AGENTS.md`
- **tRPC client/hooks:** `packages/api-client/` -- see `packages/api-client/AGENTS.md`
- **UI components:** `packages/ui/` -- see `packages/ui/AGENTS.md`
- **Next.js app (serves the tRPC endpoint):** `apps/web-next/` -- see `apps/web-next/AGENTS.md`