Skip to content

raf-query is early. The API can still change between minor releases, so use it at your own risk.

raf-query

Getting started

Examples

The same issue tracker built on TanStack Start, on the Next.js App Router, on React Router Framework Mode, and on TanStack Start with Effect, ready to open in CodeSandbox.

The repository has one app built four times: a small issue tracker on TanStack Start, on the Next.js App Router and on React Router Framework Mode, so the integrations can be compared side by side, and on TanStack Start again, written with Effect end to end. Each runs live on Vercel, and opens in CodeSandbox, which runs it on a cloud VM.

ExampleFrameworkSourceLiveTry it
Issue trackerTanStack Startexamples/tanstack-starttanstack-start.raf-query.katt.devOpen in CodeSandbox
Issue trackerNext.js App Routerexamples/nextjsnextjs.raf-query.katt.devOpen in CodeSandbox
Issue trackerReact Router Framework Modeexamples/react-routerreact-router.raf-query.katt.devOpen in CodeSandbox
Issue trackerTanStack Start, Effect and Effect RPCexamples/tanstack-start-effecttanstack-start-effect.raf-query.katt.devOpen in CodeSandbox

What they cover

Every feature in these docs appears somewhere in the app:

Screen or actionShows
Loader verbThe URL's first segment picks how loaders start queries: prefetch, ensure or fetch (/ensure/issues/3)
Progress bar along the topThe router's own pending state while ensure and fetch wait: useRouterState, useNavigation, or Next.js's useLinkStatus
Issue list with status filter and sortSearch params → loader → paged query, infinite scroll, whilePending: "previous", isPrevious dimming
Issue detailUnder prefetch the page opens onto skeletons for the title, body, and status; ensure and fetch wait for them
Comments under an issueA paged query, streamed under prefetch, infinite scroll
Assignee cardThe loader or server component starts the assignee by issue id, next to the issue, so neither waits on the other
Inline-edit title and body; change status from the listForm Actions / transitions, useOptimistic and await qc.invalidate(...)

Data comes from an in-memory database with artificial latency (400 ms ± 50% per call), behind a small REST API the TanStack Start, Next.js and React Router apps share. The top bar's Loader select switches the loader verb, and keeps the page you are on. On Next.js and React Router every navigation renders on the server with a new client, so ensure waits as long as fetch there; in the TanStack Start apps, loaders run in the browser and ensure shows cached data at once. The top bar sets that latency, how links prefetch, and how long cached data stays fresh. Reset clears those choices and the query cache, then reloads. The Effect app serves it as an Effect RPC group instead: its queryFns, loaders and form actions are Effects, and they catch its typed errors with Effect.catchTag. Data the server loads for the first render streams in with the HTML; later loads show up in the browser's network tab.

Run them locally

git clone https://github.com/KATT/raf-query
cd raf-query
pnpm install
pnpm dev:start         # TanStack Start on http://localhost:3000
pnpm dev:next          # Next.js on http://localhost:3001
pnpm dev:react-router  # React Router on http://localhost:3006
pnpm dev:effect        # TanStack Start with Effect on http://localhost:3005

Demos in these docs

The guides embed live demos that run entirely in your browser. Each has its own query client and a fake server with adjustable latency, and shows its requests as a waterfall and its cache entries as they change. Expand opens the same demo in a dialog next to the devtools panel. Good places to start: