Skip to content
IC Reactor

TanStack Router

A full-featured application demonstrating IC Reactor with TanStack Router for navigation, route loaders, and authentication.

  • Retargeting one reactor per route with setCanisterId() in a loader
  • Generated createQuery / createSuspenseQuery / createSuspenseQueryFactory / createMutation modules, one per method
  • Suspense-based data fetching with <Suspense> boundaries
  • Query invalidation after mutations with reactor.invalidateQueries()
  • Authentication with createAuthHooks
  • File-based routing structure

A single DisplayReactor is defined once and retargeted per route, so every generated query object keeps working when the user switches tokens:

src/canisters/ledger/reactor.ts
import { DisplayReactor, createActorHooks } from "@ic-reactor/react"
import { clientManager } from "../../lib/client"
import { idlFactory, type _SERVICE } from "./declarations/icrc1.did"
export type LedgerService = _SERVICE
export const ledgerReactor = new DisplayReactor<LedgerService>({
clientManager,
idlFactory,
name: "ledger",
canisterId: "ryjl3-tyaaa-aaaaa-aaaba-cai",
})
export const { useActorQuery, useActorMutation, useActorSuspenseQuery } =
createActorHooks(ledgerReactor)

The loader points the shared reactor at the canister for this route. Query keys are rooted at the reactor’s canister ID, so every cached entry is scoped automatically:

// src/routes/wallet/$canisterId.tsx
import { createFileRoute } from "@tanstack/react-router"
import { ledgerReactor } from "@/canisters/ledger/reactor"
export const Route = createFileRoute("/wallet/$canisterId")({
component: TokenWallet,
loader: async ({ params: { canisterId } }) => {
ledgerReactor.setCanisterId(canisterId)
},
})
function TokenWallet() {
// Refresh every query for the active canister
const handleRefreshAll = () => ledgerReactor.invalidateQueries()
// ...
}

Generated hook modules wrap the reactor in reusable query objects, usable both inside components and in loaders:

src/canisters/ledger/hooks/icrc1NameQuery.ts
import { createQuery } from "@ic-reactor/react"
import { ledgerReactor } from "../reactor"
export const icrc1NameQuery = createQuery(ledgerReactor, {
functionName: "icrc1_name",
})
src/components/token-name.tsx
import { icrc1NameQuery } from "@/canisters/ledger/hooks"
export function TokenName() {
const { data: name } = icrc1NameQuery.useQuery()
return <span>{name ?? "Token"}</span>
}