[v3 Beta Announcement] IC Reactor: The "Missing" Data-Fetching Library for Internet Computer

Hi everyone! :waving_hand:

I’m excited to announce the v3 Beta of IC Reactor — a modern, type-safe library designed to solve the “frontend boilerplate” when building on the Internet Computer.

If you’ve ever found yourself manually managing actors queries and mutations, handling forms, writing endless if ('ok' in result) checks to unwrap Candid variants, or trying to figure out how to cache canister responses without reinventing the wheel, IC Reactor is for you.

:rocket: The Concept: TanStack Query for the IC

Rather than building another meta-framework, IC Reactor is built directly on top of TanStack Query (formerly React Query). This means you get production-grade caching, background refetching, and optimistic updates out of the box, but specifically tailored for IC canisters.

:sparkles: Key Features in v3

  • :package: Automatic Result Unwrapping (The Pain Killer): Say goodbye to manually checking Ok vs Err variants. IC Reactor automatically unwraps Result<T, E> types. If the canister returns an Err, it’s thrown as a typed CanisterError, allowing you to use standard try/catch or hook-level error states.
  • :locked: End-to-End Type Safety: Your Candid definitions flow directly into your React hooks. Complete autocomplete and type checking from .did to UI.
  • :counterclockwise_arrows_button: Auto Transformations (DisplayReactor): Automatically handles display-ready types (BigInt → string, Principal → text, etc.) so your UI logic stays clean.
  • :atom_symbol: Power Hooks: Intuitive useActorQuery, useActorMutation, and useActorInfiniteQuery (perfect for paginated lists).
  • :locked_with_key: Seamless Auth: Built-in support for Internet Identity via createAuthHooks.
  • :robot: AI-Friendly: Includes a structured llms.txt and Cursor rules to help AI assistants write better code for your project.

:bullseye: Why use Reactor instead of a standard Actor?

Feature Standard Actor IC Reactor
Type-safe methods :white_check_mark: :white_check_mark:
Result Unwrapping :cross_mark: Manual :white_check_mark: Automatic
Error Handling :cross_mark: Generic :white_check_mark: CanisterError
Query Caching :cross_mark: :white_check_mark: Built-in
Background Refetching :cross_mark: :white_check_mark: Automatic
Identity Sharing :cross_mark: Per-actor :white_check_mark: Shared Manager

:hammer_and_wrench: Quick Look

// Candid: get_user_profile : (text) -> (variant { Ok : UserProfile; Err : text }) query;
// No more 'if (ok in result)' boilerplate!
const { data, error, isError } = useActorQuery({
  functionName: "get_user_profile",
  args: [userId],
});

if (isError) {
  // error is a typed CanisterError if the canister returned an Err variant
  console.error("Canister said no:", error.err); 
}

:test_tube: Try the Demos!

We have several live examples in the repository that showcase these features in action:

:speech_balloon: We need your feedback!

We are currently in v3.0.0-beta. The core API is stable, but we want to hear from devs building real-world DApps. If you’re tired of the manual boilerplate, please give it a spin!

We want to know:

  • Does the DisplayReactor simplify your UI code?
  • How is the experience of managing multiple canisters?
  • What are we missing to make this your go-to library for every IC project?

Links:

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

Special thanks to the @icp-sdk team (@ilbert, @peterparker) for their tireless work on a clean, type-safe SDK that powers IC Reactor.

Fantastic work, well done.

Will definitely have a look how this has been implemented, we’re doing something similar in II (.then(throwCanisterError) utility fn).

How it works internally in ic-reactor:

Here is a quick demo video I put together to show ic-reactor v3 in action! :movie_camera:

In this video, I demonstrate one of my favorite features: Optimistic UI with Automatic Rollback.

We all know the Internet Computer is fast (approx. 2s finality), but for a modern web experience, users expect instant feedback.

In the demo, you’ll see:

  1. Instant Updates: Clicking “Like” updates the UI immediately (0 latency).
  2. Chaos Mode: I simulate a canister failure (rejection), and the library automatically rolls back the state to its previous value. No complex error handling code required!

Check it out below:

Source code: ic-reactor/examples/all-in-one-demo at main · B3Pay/ic-reactor · GitHub
Related X post: https://x.com/b3hr4d_dev/status/2008232482177528092

Let me know what you think! :backhand_index_pointing_down:

This is cool, I’m definitely annoyed by ‘ok’ in result already :sweat_smile: I see TanStack has Angular integration as well, so might give that a try at some point

This is critical for competing with Web2. Users do not care (yet) whether something is onchain (unfortunately) , they just expect instant Web2 UX speed. Truly great work.

Interesting, Let me know if you have any issues with Angular integration.

Thank you so much – really appreciate your help getting me more visibility on X! :folded_hands:t2:

You deserve the praise. I only wish more of the ICP community on X focused on promoting solutions instead of obsessing over token price.

Here is Part 2 of the demo series! :movie_camera:

In this video, I dive into Advanced Data Fetching.

One common challenge on the IC is handling mixed latency. If your app calls multiple canisters—and one is slower or under load—it can often freeze the user experience.

What you’ll see in the video:

  1. Suspense Queries: I simulating a slow network response using DevTools. You’ll see how createSuspenseQuery allows the rest of the app to remain strictly interactive while the specific data loads (showing a skeleton state).
  2. Infinite Scrolling: A walkthrough of createInfiniteQuery to handle large datasets with pagination, making it feel just like a standard web2 app.

Watch it here:

Source code: ic-reactor/examples/all-in-one-demo at main · B3Pay/ic-reactor · GitHub
Related X post: https://x.com/b3hr4d_dev/status/2008500787010502821

Feedback is welcome! :rocket:

I’m excited to share a major addition to the library: Dynamic Candid Support.

We realized that while type-safety is amazing for your own canisters, sometimes you need to interact with 3rd-party canisters, build explorers, or create generic tools like B3Forge Playground where you don’t have the IDL files at compile time.

We’ve added a new package @ic-reactor/candid that lets you do exactly that.

1. Dynamic Actor Creation

You can now initialize a reactor by simply providing the Candid string (or letting it fetch from the network automatically).

import { CandidReactor } from "@ic-reactor/candid"

// Option A: Fetch IDL from the network automatically
const reactor = new CandidReactor({
  canisterId: "ryjl3-tyaaa-aaaaa-aaaba-cai", // ICP Ledger
  clientManager,
})
await reactor.initialize()

// Option B: Provide Candid string directly
const reactor = new CandidReactor({
  canisterId: "ryjl3-tyaaa-aaaaa-aaaba-cai",
  clientManager,
  candid: `service : {
    icrc1_balance_of : (record { owner : principal }) -> (nat) query;
  }`,
})
await reactor.initialize()

Once initialized, all standard Reactor features work automatically—including TanStack Query caching (fetchQuery), hooks, and type safety (generic types).

2. One-Shot Dynamic Calls

If you just need to make a single call without setting up a full reactor, we added “One-Shot” methods. These register the method signature on-the-fly and execute the call in one step.

Query with caching (TanStack Query):

const balance = await reactor.fetchQueryDynamic({
  functionName: "icrc1_balance_of",
  candid: "(record { owner : principal }) -> (nat) query",
  args: [{ owner: Principal.fromText("...") }],
})

Direct Query:

const symbol = await reactor.queryDynamic({
  functionName: "icrc1_symbol",
  candid: "() -> (text) query",
})

Update Call:

const result = await reactor.callDynamic({
  functionName: "transfer",
  candid:
    "(record { to : principal; amount : nat }) -> (variant { Ok : nat; Err : text })",
  args: [{ to: recipient, amount: 100n }],
})

Why this matters

This completely removes the need for didc or code generation steps when building generic tools or quick scripts. You can literally just paste a Candid signature string and start interacting with the blockchain.

The documentation for these new packages is now live:
https://b3pay.github.io/ic-reactor/v3/packages/candid/

We’d love to hear your thoughts on this approach!