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
pocketbaseskill 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
pocketbaseskill) - 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
npx create-tsrouter-app@latest frontend \
--toolchain biome \
--package-manager npm \
--no-git
Note:
create-tsrouter-appis 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
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:
npx shadcn@latest add button card input label
1-4. Adding TanStack Query
npm install @tanstack/react-query
1-5. Installing PocketBase JS SDK
npm install pocketbase
1-6. Installing pocketbase-typegen
npm install -D pocketbase-typegen
1-7. Verifying the Setup
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:
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 thePocketBasetype directly and replace it after generation:// 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:
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;
/apiis 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 frompb_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:
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:
{
"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
--urlto 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:
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { Outlet, createRootRouteWithContext } from "@tanstack/react-router";
export interface RouterContext {
queryClient: QueryClient;
}
export const Route = createRootRouteWithContext<RouterContext>()({
component: RootComponent,
});
function RootComponent() {
return <Outlet />;
}
Create the QueryClient in frontend/src/router.tsx (or main.tsx) and pass it to the router context:
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.tsxand 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:
- Auth context — Create an AuthProvider in
frontend/src/lib/auth.tsx - Protected routes — Authentication check using TanStack Router's
beforeLoad - Login page —
frontend/src/routes/login.tsx
Details: Read references/auth-patterns.md
3. Development Workflow
Running PocketBase and Vite Simultaneously
Terminal 1 (PocketBase):
cd backend && ./pocketbase serve --http=127.0.0.1:8090
Terminal 2 (Vite dev server):
cd frontend && npm run dev
In a Claude Code session, start PocketBase in the background:
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):
# 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 buildsucceeds -
src/lib/pocketbase.tshas been created - Proxy configuration has been added to
vite.config.ts -
components.jsonexists (Shadcn UI initialized) - QueryClient is configured (
__root.tsx/router.tsx) - PocketBase JS SDK can be imported (
import PocketBase from "pocketbase") -
npm run typegengenerates 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 |