159 lines
4.8 KiB
Markdown
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`
|