React
Typed TanStack Query hooks for React with @pyrpc/react.
@pyrpc/react wraps your pyRPC procedures in TanStack Query hooks. Every procedure becomes a fully typed useQuery or useMutation -- no hand-written types, no schema files.
Installation
npm install @pyrpc/react @tanstack/react-queryThat's the only install you need. @pyrpc/types and @pyrpc/client ship as dependencies of every adapter, and pyrpc dev generates your types into __pyrpc.ts and wires the @pyrpc/types import to it automatically.
Project structure
my-app/
src/
lib/
pyrpc.ts <- create the client once here
index.tsx <- wrap root in api.Provider
App.tsx <- call api.greet.useQuery() etc.
__pyrpc.ts <- written by pyrpc dev (do not edit)1. Create the client
import { createReactClient, httpBatchLink } from "@pyrpc/react"
import type { Types } from "@pyrpc/types"
export const api = createReactClient<Types>({
links: [
httpBatchLink({
url: process.env.REACT_APP_API_URL ?? "http://localhost:8000",
}),
],
})api is a single object. It carries every procedure as a hook, plus api.Provider for the TanStack Query cache.
2. Add the Provider
Wrap your root component once -- this sets up the shared TanStack Query cache:
import React from "react"
import ReactDOM from "react-dom/client"
import App from "./App"
import { api } from "./lib/pyrpc"
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<api.Provider>
<App />
</api.Provider>
</React.StrictMode>
)3. Call procedures
import { useState } from "react"
import { api } from "./lib/pyrpc"
export function App() {
const [name, setName] = useState("")
// @rpc.query maps to useQuery
const { data: greeting, isLoading } = api.greet.useQuery({ name: "World" })
// @rpc.mutation maps to useMutation
const createItem = api.create_item.useMutation()
return (
<div>
{isLoading ? <p>Loading</p> : <pre>{JSON.stringify(greeting)}</pre>}
<input value={name} onChange={e => setName(e.target.value)} />
<button
onClick={() => createItem.mutate({ name })}
disabled={createItem.isPending}
>
{createItem.isPending ? "Creating" : "Create"}
</button>
{createItem.isSuccess && <pre>{JSON.stringify(createItem.data)}</pre>}
</div>
)
}useQuery and useMutation are the standard TanStack Query hooks. All their options (enabled, staleTime, onSuccess, etc.) work exactly as documented in the TanStack Query docs.
Invalidating after a mutation
const utils = api.useUtils()
const createItem = api.create_item.useMutation({
onSuccess: () => {
utils.list_items.invalidate()
},
})TypeScript
All parameter types, return types, and error shapes are inferred from your Python function signatures. There is nothing extra to annotate.
// Python: @rpc.query \n def greet(name: str) -> dict: ...
api.greet.useQuery({ name: "Ada" }) // correct
api.greet.useQuery({ nme: "Ada" }) // type error -- typo caught at compile timeClient config
createReactClient accepts the same config as the vanilla client:
| Option | Type | Default | Description |
|---|---|---|---|
links | Link[] | required | Link pipeline; exactly one terminating link (httpLink or httpBatchLink) |
kinds | ProcedureKindMap | generated | Override generated procedure kinds |
Full working examples
| Server | Source |
|---|---|
| FastAPI | examples/fastapi-react |
| Flask | examples/flask-react |
| Django | examples/django-react |

pyRPC