Skip to content

IC-Reactor: A suite of JavaScript libraries for seamless frontend development on the Internet Computer platform, offering state management, React integration, and core functionalities for efficient blockchain interactions.

Notifications You must be signed in to change notification settings

B3Pay/ic-reactor

Repository files navigation

IC Reactor

IC Reactor Logo

Type-safe Internet Computer integration for TypeScript and React

npm version License: MIT TypeScript


IC Reactor is a monorepo of libraries for building Internet Computer (ICP) apps with:

  • end-to-end TypeScript types
  • TanStack Query-powered caching and refetching
  • React hook factories (useActorQuery, useActorMutation, etc.)
  • display-friendly transforms (DisplayReactor)
  • optional code generation (CLI + Vite plugin)

Why IC Reactor

IC Reactor gives you a higher-level API than raw Actor usage while keeping type safety and control:

  • typed canister method calls
  • built-in cache keys and invalidation primitives
  • typed Ok/Err result handling
  • shared auth/agent management via ClientManager
  • reusable query/mutation objects that work both inside and outside React

Package Overview

Package Purpose
@ic-reactor/core Core runtime (ClientManager, Reactor, DisplayReactor, cache integration)
@ic-reactor/react React hooks + query/mutation factories
@ic-reactor/candid Dynamic Candid parsing and runtime reactors
@ic-reactor/parser Local Candid parser (WASM-based)
@ic-reactor/codegen Shared codegen pipeline used by CLI and Vite plugin
@ic-reactor/cli Generate declarations + typed hooks/reactors
@ic-reactor/vite-plugin Vite plugin for watch-mode hook generation

Install

React apps

pnpm add @ic-reactor/react @icp-sdk/core @tanstack/react-query

Non-React apps

pnpm add @ic-reactor/core @icp-sdk/core @tanstack/query-core

Optional packages

# Internet Identity auth helpers
pnpm add @icp-sdk/auth

# Dynamic Candid support (explorers/dev tools)
pnpm add @ic-reactor/candid @ic-reactor/parser

Quick Start (React)

1. Create a shared client manager and reactor

// src/reactor.ts
import { ClientManager, Reactor } from "@ic-reactor/react"
import { QueryClient } from "@tanstack/react-query"
import { idlFactory, type _SERVICE } from "./declarations/my_canister"

export const queryClient = new QueryClient()

export const clientManager = new ClientManager({
  queryClient,
  // withCanisterEnv: true, // optional: useful in local/dev setups
})

export const backendReactor = new Reactor<_SERVICE>({
  clientManager,
  idlFactory,
  name: "backend",
  canisterId: "rrkah-fqaaa-aaaaa-aaaaq-cai",
})

2. Create hooks

// src/hooks.ts
import { createActorHooks, createAuthHooks } from "@ic-reactor/react"
import { backendReactor, clientManager } from "./reactor"

export const {
  useActorQuery,
  useActorMutation,
  useActorSuspenseQuery,
  useActorInfiniteQuery,
} = createActorHooks(backendReactor)

export const { useAuth, useUserPrincipal } = createAuthHooks(clientManager)

3. Use in React components

// src/App.tsx
import { QueryClientProvider } from "@tanstack/react-query"
import { queryClient } from "./reactor"
import { useActorQuery, useActorMutation, useAuth } from "./hooks"

function Greeting() {
  const { data, isPending, error } = useActorQuery({
    functionName: "greet",
    args: ["World"],
  })

  if (isPending) return <div>Loading...</div>
  if (error) return <div>Error: {error.message}</div>

  return <h1>{data}</h1>
}

function AuthButton() {
  const { login, logout, isAuthenticated, principal } = useAuth()

  return isAuthenticated ? (
    <button onClick={() => logout()}>
      Logout {principal?.toText().slice(0, 8)}...
    </button>
  ) : (
    <button onClick={() => login()}>Login</button>
  )
}

function UpdateProfileButton() {
  const { mutate, isPending } = useActorMutation({
    functionName: "update_profile",
  })

  return (
    <button
      disabled={isPending}
      onClick={() => mutate([{ name: "Alice", bio: "Hello IC" }])}
    >
      {isPending ? "Saving..." : "Save"}
    </button>
  )
}

export function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <AuthButton />
      <Greeting />
      <UpdateProfileButton />
    </QueryClientProvider>
  )
}

Core Usage Patterns

Pattern A: Generic hooks with createActorHooks(...)

Use when component code can pass functionName and args inline.

  • Best for straightforward React integration
  • Single typed hook suite per reactor

Pattern B: Reusable query/mutation factories (recommended for shared use)

Use when the same operation must be used:

  • inside React components
  • in route loaders/actions
  • in services or test helpers
import { createQuery, createMutation } from "@ic-reactor/react"
import { backendReactor } from "./reactor"

export const getProfile = createQuery(backendReactor, {
  functionName: "get_profile",
})

export const updateProfile = createMutation(backendReactor, {
  functionName: "update_profile",
  invalidateQueries: [getProfile.getQueryKey()],
})

Inside React:

const { data } = getProfile.useQuery()
const { mutateAsync } = updateProfile.useMutation({
  onSettled: () => toast.success("Profile updated!"),
})

Outside React:

await getProfile.fetch()
const cached = getProfile.getCacheData()
await updateProfile.execute([{ name: "Alice" }])

Important: Do not call React hooks (useActorQuery, .useQuery(), .useMutation()) outside React components or custom hooks.

Pattern C: DisplayReactor for UI-friendly values

Use DisplayReactor when you want transformed values for UI/forms (for example, bigint and Principal represented as strings).

import { DisplayReactor } from "@ic-reactor/react"

Code Generation (CLI and Vite Plugin)

For larger canisters or frequent .did changes, prefer generated hooks.

Vite plugin (recommended for Vite apps)

// vite.config.ts
import { defineConfig } from "vite"
import react from "@vitejs/plugin-react"
import { icReactor } from "@ic-reactor/vite-plugin"

export default defineConfig({
  plugins: [
    react(),
    icReactor({
      canisters: [{ name: "backend", didFile: "./backend/backend.did" }],
    }),
  ],
})

CLI (explicit generation / non-Vite)

npx @ic-reactor/cli init
npx @ic-reactor/cli generate

Generated query/mutation files typically expose both:

  • React methods (.useQuery(), .useMutation())
  • imperative methods (.fetch(), .execute(), .invalidate())

Dynamic Candid (Explorers and Dev Tools)

import { CandidDisplayReactor } from "@ic-reactor/candid"
import { ClientManager } from "@ic-reactor/core"

const clientManager = new ClientManager()

const reactor = new CandidDisplayReactor({
  canisterId: "ryjl3-tyaaa-aaaaa-aaaba-cai",
  clientManager,
})

await reactor.initialize()

const balance = await reactor.callMethod({
  functionName: "icrc1_balance_of",
  args: [{ owner: "aaaaa-aa" }],
})

console.log(balance)

Reactor vs Standard Actor (Summary)

Feature Standard Actor IC Reactor
Type-safe method calls
Query caching
Background refetching
Typed Ok/Err handling ❌ (manual)
Shared auth/identity + cache coordination ✅ (ClientManager)
Display-friendly transforms ✅ (DisplayReactor)

Examples

Example Description
all-in-one-demo End-to-end demo with queries, mutations, suspense, infinite queries
tanstack-router Router loaders/actions + generated hooks
query-demo Query and mutation factory patterns
multiple-canister Shared auth across multiple canisters
ckbtc-wallet More advanced canister integrations
codegen-in-action CLI vs Vite plugin codegen comparison
typescript-demo Core usage without React
candid-parser Dynamic Candid parsing

Documentation

Run docs locally:

cd docs
pnpm install
pnpm dev

Development

# Install dependencies
pnpm install

# Build packages
pnpm build

# Run package tests
pnpm test

# Run e2e tests
pnpm test-e2e

# Build docs
pnpm docs:build

AI and Agent Integration

This repository is intentionally structured to work well with AI coding assistants and agents.

AI context files

Installable Skill: ic-reactor-hooks

The canonical installable IC Reactor hooks skill lives in the skills mono-repo:

Use it when asking an agent to:

  • create/refactor createActorHooks(...) integrations
  • build reusable createQuery / createMutation modules
  • explain inside-React vs outside-React usage (fetch, execute, invalidate)
  • choose between manual hooks and generated hooks (CLI / Vite plugin)

Example prompt:

Use $ic-reactor-hooks to create a reusable query/mutation factory pair for my canister and show usage both inside a React component and in a route loader.

Example install:

npx skills add B3Pay/ic-reactor-skills --full-depth --skill ic-reactor-hooks

Contributing

See CONTRIBUTING.md for development workflow, formatting, release notes, and AI-assisted contribution guidance.

Please also review the Code of Conduct.

License

MIT © Behrad Deylami


Built for the Internet Computer

About

IC-Reactor: A suite of JavaScript libraries for seamless frontend development on the Internet Computer platform, offering state management, React integration, and core functionalities for efficient blockchain interactions.

Topics

Resources

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 6