packages/cli/skills/pikku-react/SKILL.md
Set up @pikku/react in a React app: PikkuProvider context, createPikku factory, and the usePikkuRPC / usePikkuFetch hooks for direct (non-React-Query) calls. TRIGGER when: the user is bootstrapping a React frontend that talks to a Pikku backend, asks how to wire `PikkuProvider`, or needs to make one-off RPC calls outside of useQuery/useMutation. DO NOT TRIGGER when: the user is asking about useQuery/useMutation hooks (use pikku-react-query) or about workflows (use pikku-workflows-client).
npx skillsauth add pikkujs/pikku pikku-reactInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Use this skill as an execution checklist, not reference material.
pikku-meta when available; otherwise run the relevant pikku meta ... --json command and inspect only the focused output you need..pikku, node_modules, vendored packages, or broad build artifacts.pikku-verify or pikku all when functions, wirings, schemas, or generated clients may have changed.@pikku/react is the smallest possible binding: a Context provider plus
two hooks. It does not depend on React Query — that's a separate
opt-in via the generated api.gen.ts. Use this skill when setting up the
provider or making direct RPC calls.
import {
PikkuProvider,
createPikku,
usePikkuFetch,
usePikkuRPC,
usePikkuRealtime,
} from '@pikku/react'
Five exports. usePikkuRealtime is only valid when you wired a
PikkuRealtime class via createPikku — see step 3 below.
import { createPikku, PikkuProvider } from '@pikku/react'
import { PikkuFetch } from './pikku/pikku-fetch.gen'
import { PikkuRPC } from './pikku/pikku-rpc.gen'
const pikku = createPikku(PikkuFetch, PikkuRPC, {
serverUrl: import.meta.env.VITE_API_URL ?? 'http://localhost:3000',
})
createRoot(document.getElementById('root')!).render(
<PikkuProvider pikku={pikku}>
<App />
</PikkuProvider>
)
If the project also exposes realtime events (see pikku-realtime), pass
the PikkuRealtime class as the third argument and the instance gets a
realtime field too:
import { PikkuRealtime } from './pikku/realtime.gen'
const pikku = createPikku(PikkuFetch, PikkuRPC, PikkuRealtime, {
serverUrl: import.meta.env.VITE_API_URL ?? 'http://localhost:3000',
})
// pikku.fetch / pikku.rpc / pikku.realtime — all share the same fetch
// (server URL + auth configured once).
The generated classes come from your pikku.config.json:
| config field | generated file |
| ---------------------------- | ----------------------------------------------------- |
| clientFiles.fetchFile | typed HTTP client (PikkuFetch class) |
| clientFiles.rpcWiringsFile | RPC client (PikkuRPC class) calling all exposed fns |
| clientFiles.realtimeFile | PikkuRealtime (websocket events + SSE + channels) |
If a file isn't being generated, that field is missing from the config —
add it and re-run pikku all.
createPikku(...) accepts the same CorePikkuFetchOptions as PikkuFetch
plus serverUrl. Auth headers, request interceptors, etc. are configured
on the fetch instance — RPC and realtime inherit them automatically.
Inside a component:
import { usePikkuRPC } from '@pikku/react'
function Logout() {
const rpc = usePikkuRPC()
return <button onClick={() => rpc.invoke('logoutUser', {})}>Sign out</button>
}
rpc.invoke(name, data) is typed against FlattenedRPCMap — name must
be an exposed function id, data matches the input schema, return value
matches the output schema.
You also have rpc.<funcName>(data) if the generated RPC client builds
direct methods (project-dependent).
const fetch = usePikkuFetch()
const data = await fetch.get('/some-rest-route', { searchParams: {...} })
Use this only when the function is wired via HTTP (REST shape) and you
need a path-style call. For RPC calls, usePikkuRPC() is cleaner.
If you wired a PikkuRealtime class into createPikku, use
usePikkuRealtime() to grab the shared instance:
import { usePikkuRealtime } from '@pikku/react'
import type { PikkuRealtime } from './pikku/realtime.gen'
function TodoList() {
const realtime = usePikkuRealtime<PikkuRealtime>()
useEffect(() => {
return realtime.subscribe('todo-created', ({ todo }) => {
/* ... */
})
}, [realtime])
// ...
}
The hook throws if no PikkuRealtime was wired — that's how you know to
add it to createPikku(...). Full event-hub setup, publishing, and SSE
helpers live in pikku-realtime.
| Need | Use |
| ----------------------------------- | --------------------------------------------- |
| Render data, dedupe + cache | usePikkuQuery (react-query) |
| Trigger a write, wait for result | usePikkuMutation (react-query) |
| Paginate | usePikkuInfiniteQuery (react-query) |
| One-off call from an event handler | usePikkuRPC() direct |
| Hit a REST endpoint (not RPC) | usePikkuFetch() |
| Run a workflow | pikku-workflows-client |
| Subscribe to events / SSE / channel | usePikkuRealtime() (see pikku-realtime) |
The first three live in your generated api.gen.ts (see the
pikku-react-query skill). This skill covers the bottom four rows.
Auth is handled at the PikkuFetch layer — pass options to createPikku
or set headers on the fetch instance after creation. Common pattern:
const pikku = createPikku(PikkuFetch, PikkuRPC, {
serverUrl: '...',
fetchOptions: {
onRequest: (req) => {
const token = localStorage.getItem('token')
if (token) req.headers.set('Authorization', `Bearer ${token}`)
},
},
})
Exact option names depend on the @pikku/fetch version — read
PikkuFetch's constructor type if unsure.
PikkuFetch/PikkuRPC inside a component — createPikku
goes once at the app root, the instance flows through Context.usePikkuRPC() outside a <PikkuProvider> — it throws.documentation
Deprecated — use pikku-middleware instead. Tag middleware (addTagMiddleware) is now documented as a section within the pikku-middleware skill, alongside global HTTP middleware, execution order, and the service-to-service bearer auth pattern.
testing
Use when adding authorization checks to Pikku functions or routes — pikkuPermission, pikkuAuth, per-function permissions, pattern-based permissions, or understanding OR/AND permission logic. TRIGGER when: user wants to restrict who can call a function, check resource ownership, add role-based access, or understand where permission checks belong. DO NOT TRIGGER when: user asks about middleware or request interception (use pikku-middleware), authentication strategies (use pikku-security), or session management.
testing
Use when adding any middleware to a Pikku app — global HTTP middleware, tag-scoped middleware (including service-to-service bearer auth), per-route middleware, session-setting middleware, or understanding middleware execution order and priority. TRIGGER when: user wants middleware on some or all routes, machine-to-machine auth, tag-scoped cross-cutting concerns, global interceptors, or middleware priority/order questions. DO NOT TRIGGER when: user asks about permissions/authorization checks (use pikku-permissions), auth strategies like authBearer/authCookie (use pikku-security), or deployment.
documentation
Standard cleanup to run right after a Pikku template is cloned or scaffolded into a new project. TRIGGER when: a Pikku template was just cloned/scaffolded (via `pikku create`, `git clone <template>`, or the user says "I cloned the kanban template / starter / template"), or the working tree still looks like an untouched template (template README, placeholder `@project/*` name in package.json). DO NOT TRIGGER when: working in an established project mid-feature, or editing the template repo itself.