--- name: pb-react-spa description: >- React SPA frontend setup skill (PocketBase integration). Vite + TanStack Router (file-based routing) + TanStack Query + Tailwind CSS / Shadcn UI + Biome. Use when creating a React frontend, SPA setup, PocketBase UI, frontend project initialization, React app, or dashboard build. license: MIT metadata: version: "1.0.0" allowed-tools: Read Write Edit Bash Grep Glob --- # PocketBase React SPA Skill A skill that automates the setup of a React SPA frontend integrated with a PocketBase backend. **Tech Stack:** - **Vite** — Build tool / dev server - **React + TypeScript** — UI framework - **TanStack Router** — File-based routing - **TanStack Query** — Server state management - **Tailwind CSS + Shadcn UI** — Styling / UI components - **Biome** — Linter / Formatter - **PocketBase JS SDK** — Backend communication - **pocketbase-typegen** — Type generation ## Skill Resources - **References**: `references/` — Integration patterns, authentication, type generation, development & deployment guides - **PocketBase backend skill**: The `pocketbase` skill handles backend management (collection CRUD, API rule design, etc.) For details on the PocketBase JS SDK, also see `references/js-sdk.md` in the PocketBase skill. ## 0. Prerequisites - **Node.js** v18 or higher, npm - **PocketBase** is running (see the Bootstrap section in the `pocketbase` skill) - PocketBase collection design is complete (recommended) ## 1. Project Scaffolding ### 1-1. Project Structure Adopt a monorepo structure with separated frontend and backend: ``` project-root/ ├── frontend/ ← React SPA (created by this skill) ├── backend/ ← PocketBase (managed by the pocketbase skill) │ ├── pocketbase │ ├── pb_data/ │ ├── pb_migrations/ │ └── .env └── README.md ``` > **Note:** Check the user's existing project structure and adapt accordingly. The `frontend/` directory name can be changed based on user preference. ### 1-2. Base Project Generation ```bash npx create-tsrouter-app@latest frontend \ --toolchain biome \ --package-manager npm \ --no-git ``` > **Note:** `create-tsrouter-app` is a TanStack CLI wrapper where `--router-only` (file-based routing) is enabled by default. Tailwind CSS v4 is also set up automatically. This command sets up the following: - Vite + React + TypeScript - TanStack Router (file-based routing) - Tailwind CSS v4 - Biome (Linter / Formatter) ### 1-3. Adding Shadcn UI ```bash cd frontend && npx shadcn@latest init -d ``` The `-d` flag uses default settings (new-york style, neutral color). A `components.json` file is created, enabling you to add components to `@/components/ui/`. Example of adding components: ```bash npx shadcn@latest add button card input label ``` ### 1-4. Adding TanStack Query ```bash npm install @tanstack/react-query ``` ### 1-5. Installing PocketBase JS SDK ```bash npm install pocketbase ``` ### 1-6. Installing pocketbase-typegen ```bash npm install -D pocketbase-typegen ``` ### 1-7. Verifying the Setup ```bash npm run build ``` Verify that the build completes successfully. To check the dev server, run `npm run dev`. ## 2. Post-Setup Configuration After scaffolding, add the following configurations in order. ### 2-1. PocketBase Client Create **`frontend/src/lib/pocketbase.ts`**: ```ts import PocketBase from "pocketbase"; import type { TypedPocketBase } from "../types/pocketbase-types"; export const pb = new PocketBase() as TypedPocketBase; pb.autoCancellation(false); ``` > **TypedPocketBase** is the type generated by `pocketbase-typegen`. If types haven't been generated yet, temporarily use the `PocketBase` type directly and replace it after generation: > > ```ts > // Temporary version before type generation > import PocketBase from "pocketbase"; > export const pb = new PocketBase(); > pb.autoCancellation(false); > ``` ### 2-2. Vite Proxy Configuration Edit **`frontend/vite.config.ts`** to add `server.proxy`. The generated `vite.config.ts` has the following structure: ```ts import { defineConfig } from "vite"; import { devtools } from "@tanstack/devtools-vite"; import tsconfigPaths from "vite-tsconfig-paths"; import { tanstackRouter } from "@tanstack/router-plugin/vite"; import viteReact from "@vitejs/plugin-react"; import tailwindcss from "@tailwindcss/vite"; const config = defineConfig({ plugins: [ devtools(), tsconfigPaths({ projects: ["./tsconfig.json"] }), tailwindcss(), tanstackRouter({ target: "react", autoCodeSplitting: true }), viteReact(), ], // ↓ Add this server: { proxy: { "/api": { target: "http://127.0.0.1:8090", changeOrigin: true, }, "/_": { target: "http://127.0.0.1:8090", changeOrigin: true, }, }, }, }); export default config; ``` > `/api` is the PocketBase REST API, and `/_` is for PocketBase internal endpoints (realtime SSE, etc.). > > **Why no URL in the PocketBase client?** The proxy makes all PocketBase API requests same-origin during development (browser sees `localhost:5173/api/...`). In production, PocketBase serves the SPA from `pb_public/`, which is also same-origin. Since both environments are same-origin, `new PocketBase()` (no arguments) works everywhere — no environment variables needed. **Important:** Do not modify the generated plugins array. Only add `server.proxy`. ### 2-3. TypeScript Type Generation Generate types while PocketBase is running: ```bash npx pocketbase-typegen --url http://127.0.0.1:8090 --email admin@example.com --password yourpassword --out frontend/src/types/pocketbase-types.ts ``` Add an npm script to **`frontend/package.json`**: ```json { "scripts": { "typegen": "pocketbase-typegen --url http://127.0.0.1:8090 --email admin@example.com --password yourpassword --out src/types/pocketbase-types.ts" } } ``` > Confirm the superuser email and password with the user. Adjust `--url` to match the PocketBase URL. **Details:** `Read references/typegen.md` ### 2-4. TanStack Query Configuration Create a QueryClient and set up the Provider in the root component. Edit **`frontend/src/routes/__root.tsx`**: ```tsx import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { Outlet, createRootRouteWithContext } from "@tanstack/react-router"; export interface RouterContext { queryClient: QueryClient; } export const Route = createRootRouteWithContext()({ component: RootComponent, }); function RootComponent() { return ; } ``` Create the QueryClient in **`frontend/src/router.tsx`** (or `main.tsx`) and pass it to the router context: ```tsx import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { createRouter as createTanStackRouter } from "@tanstack/react-router"; import { routeTree } from "./routeTree.gen"; const queryClient = new QueryClient(); export function getRouter() { const router = createTanStackRouter({ routeTree, scrollRestoration: true, defaultPreload: "intent", defaultPreloadStaleTime: 0, context: { queryClient }, }); return router; } ``` > **Important:** Check the existing contents of `router.tsx` / `main.tsx` and integrate QueryClient while preserving existing configuration. The above is a pattern example — edit according to the generated file's code. **Details:** `Read references/react-query-pocketbase.md` ### 2-5. Authentication Integration If authentication is needed, configure the following: 1. **Auth context** — Create an AuthProvider in `frontend/src/lib/auth.tsx` 2. **Protected routes** — Authentication check using TanStack Router's `beforeLoad` 3. **Login page** — `frontend/src/routes/login.tsx` **Details:** `Read references/auth-patterns.md` ## 3. Development Workflow ### Running PocketBase and Vite Simultaneously **Terminal 1 (PocketBase):** ```bash cd backend && ./pocketbase serve --http=127.0.0.1:8090 ``` **Terminal 2 (Vite dev server):** ```bash cd frontend && npm run dev ``` > In a Claude Code session, start PocketBase in the background: > ```bash > cd backend && nohup ./pocketbase serve --http=127.0.0.1:8090 > pb.log 2>&1 & > ``` ### Development Tips - The Vite proxy eliminates CORS issues (operates as same-origin) - Frontend changes are reflected instantly via HMR - After PocketBase collection changes, regenerate types with `npm run typegen` - The Admin UI is directly accessible at `http://127.0.0.1:8090/_/` **Details:** `Read references/dev-and-deploy.md` ## 4. Deployment ### SPA Build → PocketBase pb_public Placement In production, PocketBase serves the SPA directly (no separate web server needed): ```bash # Build cd frontend && npm run build # Place in PocketBase's pb_public cp -r frontend/dist/* backend/pb_public/ ``` PocketBase automatically serves files from `pb_public/` and supports SPA client-side routing (non-existent paths fall back to `index.html`). ### Docker / Docker Compose / Reverse Proxy For production deployment including Dockerfiles (binary mode & Go package mode), Docker Compose, Caddy/Nginx reverse proxy, and executable distribution: **Details:** `Read references/deployment.md` ## 5. Setup Checklist Verification items after scaffolding is complete: - [ ] `npm run build` succeeds - [ ] `src/lib/pocketbase.ts` has been created - [ ] Proxy configuration has been added to `vite.config.ts` - [ ] `components.json` exists (Shadcn UI initialized) - [ ] QueryClient is configured (`__root.tsx` / `router.tsx`) - [ ] PocketBase JS SDK can be imported (`import PocketBase from "pocketbase"`) - [ ] `npm run typegen` generates types (when PocketBase is running) - [ ] If authentication is needed: AuthProvider and protected routes are configured ## 6. Reference Index | Topic | Reference | |-------|-----------| | TanStack Query + PB integration | `Read references/react-query-pocketbase.md` | | Authentication patterns | `Read references/auth-patterns.md` | | TypeScript type generation | `Read references/typegen.md` | | Development workflow | `Read references/dev-and-deploy.md` | | Production deployment (Docker, binary, proxy) | `Read references/deployment.md` | | JS SDK details | `Read references/js-sdk.md` in the PocketBase skill |