docs(agents): write per-app AGENTS.md for cms, web-next, web-tanstack, storybook
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
# apps/web-tanstack -- TanStack Start Reference App
|
||||
# AGENTS.md — apps/web-tanstack
|
||||
|
||||
TanStack Start reference application using TanStack Router with file-based routing. Demonstrates that feature packages are framework-agnostic by consuming the same features as Next.js (via `@repo/core-api`) using TanStack's architecture instead.
|
||||
|
||||
## 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`.
|
||||
Proof that features are framework-portable. This app consumes the exact same feature packages and tRPC routers as `apps/web-next`, but through TanStack Start's server/client architecture instead of Next.js App Router.
|
||||
|
||||
## Port: 3002
|
||||
|
||||
@@ -10,149 +12,107 @@ TanStack Start reference application using TanStack Router with file-based routi
|
||||
pnpm dev --filter @repo/web-tanstack # http://localhost:3002
|
||||
```
|
||||
|
||||
Requires tRPC endpoint (from `apps/web-next` or another backend):
|
||||
|
||||
```bash
|
||||
pnpm dev --filter @repo/web-next # Serves tRPC at http://localhost:3000/api/trpc
|
||||
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 (`/`) |
|
||||
| `src/routes/__root.tsx` | Root layout — wraps all routes with `<TrpcProvider>` from `@repo/core-trpc/tanstack` |
|
||||
| `src/routes/index.tsx` | Home page (`/`) |
|
||||
| `src/routes/blog/index.tsx` | Blog listing (`/blog`) |
|
||||
| `src/routes/blog/$slug.tsx` | Dynamic blog post (`/blog/:slug`) |
|
||||
| `e2e/` | Playwright end-to-end tests |
|
||||
|
||||
## File-Based Routing
|
||||
|
||||
TanStack Router uses file-based routing where file paths in `src/routes/` map directly to URL paths:
|
||||
TanStack Router uses file-based routing where file paths map directly to URL routes:
|
||||
|
||||
| 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) |
|
||||
| File | URL |
|
||||
|---|---|
|
||||
| `src/routes/__root.tsx` | Root (all routes) |
|
||||
| `src/routes/index.tsx` | `/` |
|
||||
| `src/routes/blog/index.tsx` | `/blog` |
|
||||
| `src/routes/blog/$slug.tsx` | `/blog/:slug` |
|
||||
|
||||
### 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
|
||||
Naming conventions:
|
||||
- `__root.tsx` — special root layout
|
||||
- `index.tsx` — index route for its directory
|
||||
- `$paramName.tsx` — dynamic segment
|
||||
|
||||
## Provider Setup
|
||||
## tRPC Setup
|
||||
|
||||
The `<ApiProvider>` wraps the entire app in `__root.tsx`:
|
||||
The root route wraps with `<TrpcProvider>`:
|
||||
|
||||
```tsx
|
||||
```typescript
|
||||
// src/routes/__root.tsx
|
||||
import { Outlet, createRootRoute } from "@tanstack/react-router";
|
||||
import { ApiProvider } from "@repo/api-client";
|
||||
import { TrpcProvider } from "@repo/core-trpc/tanstack";
|
||||
import { Outlet } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createRootRoute({
|
||||
component: () => (
|
||||
<ApiProvider trpcUrl="http://localhost:3000/api/trpc">
|
||||
<TrpcProvider trpcUrl="http://localhost:3000/api/trpc">
|
||||
<Outlet />
|
||||
</ApiProvider>
|
||||
</TrpcProvider>
|
||||
),
|
||||
});
|
||||
```
|
||||
|
||||
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.
|
||||
Note: `trpcUrl` must point to a running tRPC endpoint (e.g., from `apps/web-next`).
|
||||
|
||||
## Recipe: Adding a New Route with Data Fetching
|
||||
## Fetching data in routes
|
||||
|
||||
This example adds an `/articles` route that lists published articles.
|
||||
|
||||
### Step 1: Create the route file
|
||||
|
||||
Create `src/routes/articles/index.tsx`:
|
||||
|
||||
```tsx
|
||||
```typescript
|
||||
// src/routes/blog/$slug.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 { useTRPC } from "@repo/core-trpc";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
export const Route = createFileRoute("/articles/$id")({
|
||||
component: ArticlePage,
|
||||
export const Route = createFileRoute("/blog/$slug")({
|
||||
component: BlogPostPage,
|
||||
});
|
||||
|
||||
function ArticlePage() {
|
||||
const { id } = Route.useParams();
|
||||
function BlogPostPage() {
|
||||
const { slug } = 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 })
|
||||
trpc.blog.getBySlug.queryOptions({ slug })
|
||||
);
|
||||
|
||||
if (isLoading) return <p>Loading...</p>;
|
||||
|
||||
return (
|
||||
<main>
|
||||
<h1>Article {id}</h1>
|
||||
</main>
|
||||
);
|
||||
return <article>{data?.title}</article>;
|
||||
}
|
||||
```
|
||||
|
||||
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 |
|
||||
| `@repo/core-api` | AppRouter type (via core-trpc) |
|
||||
| `@repo/core-trpc/tanstack` | TanStack tRPC client + provider |
|
||||
| `@repo/core-ui` | Design system components |
|
||||
| `@tanstack/react-router` | File-based routing |
|
||||
| `@tanstack/react-query` | Data fetching + caching |
|
||||
| `react` / `react-dom` | React 19 runtime |
|
||||
|
||||
## Test conventions
|
||||
|
||||
- e2e tests in `e2e/` folder: `*.spec.ts`
|
||||
- Playwright config in `e2e/playwright.config.ts`
|
||||
- Run: `pnpm test:e2e` (both Next.js and TanStack)
|
||||
|
||||
Parallel to `apps/web-next` e2e: validates that features work across frameworks.
|
||||
|
||||
## 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`
|
||||
- **Feature packages:** `packages/{auth,blog,media,marketing-pages,navigation}/`
|
||||
- **tRPC composition:** `packages/core-api/AGENTS.md`
|
||||
- **tRPC client + provider:** `packages/core-trpc/AGENTS.md`
|
||||
- **UI components:** `packages/core-ui/AGENTS.md`
|
||||
- **Next.js app (serves tRPC):** `apps/web-next/AGENTS.md`
|
||||
|
||||
Reference in New Issue
Block a user