npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ic-use-actor

v0.4.0

Published

React Hook and context provider to make interacting with Internet Computer canisters more fun!

Readme

ic-use-actor

A React hook library for interacting with Internet Computer (IC) canisters. ic-use-actor provides a simple, type-safe way to interact with IC actors using XState stores for state management.

version downloads

Features

  • Simple API: Just one function call to create a typed hook for your canister
  • No Provider Hell: No need for React Context or Provider components
  • Type Safety: Full TypeScript support with canister service definitions
  • Request/Response Interceptors: Process requests and responses with customizable callbacks
  • Global State Management: Powered by XState stores for predictable state management
  • Multiple Canisters: Easy to work with multiple canisters without nesting providers

Table of Contents

Installation

npm install ic-use-actor @icp-sdk/core

or

yarn add ic-use-actor @icp-sdk/core

or

pnpm add ic-use-actor @icp-sdk/core 

Quick Start

// 1. Create your actor hook
import { createActorHook } from "ic-use-actor";
import { canisterId, idlFactory } from "./declarations/my_canister";
import { _SERVICE } from "./declarations/my_canister/my_canister.did";

export const useMyCanister = createActorHook<_SERVICE>({
  canisterId,
  idlFactory,
});

// 2. Use it in your components
function MyComponent() {
  const { actor: myCanister, authenticate, isAuthenticated, status, isInitializing, isSuccess, isError, error } = useMyCanister();
  const { identity } = useInternetIdentity(); // or any identity provider

  // Authenticate when identity is available (keeps initialization separate from authentication)
  useEffect(() => {
    if (identity) {
      void authenticate(identity);
    }
  }, [identity, authenticate]);

  const handleClick = async () => {
    if (!myCanister) return;
    const result = await myCanister.myMethod();
    console.log(result);
  };

  if (error) return <div>Error: {error.message}</div>;
  if (isInitializing) return <div>Loading...</div>;
  if (!isAuthenticated) return <div>Please sign in</div>;

  return <button onClick={handleClick}>Call Canister</button>;
}

// 3. That's it!
function App() {
  return <MyComponent />;
}

Usage

Basic Setup

Create a hook for your canister by calling createActorHook with your canister's configuration:

// actors.ts
import { createActorHook } from "ic-use-actor";
import { canisterId, idlFactory } from "./declarations/backend";
import { _SERVICE } from "./declarations/backend/backend.did";

export const useBackendActor = createActorHook<_SERVICE>({
  canisterId,
  idlFactory,
});

Using in Components

The hook returns an object with the actor instance and several utility functions:

function MyComponent() {
  const {
    actor,           // The actor instance (initialized with anonymous agent by default)
    authenticate,    // Function to authenticate the actor with an identity
    setInterceptors, // Function to set up interceptors
    isAuthenticated, // Boolean indicating if actor is authenticated
    status,          // 'initializing' | 'success' | 'error'
    isInitializing,  // status === 'initializing'
    isSuccess,       // status === 'success'
    isError,         // status === 'error'
    error,           // Any error that occurred during initialization
    reset,           // Function to reset the actor state
    clearError       // Function to clear error state
  } = useBackendActor();

  const { identity } = useInternetIdentity();

  // Authenticate when identity is available
  useEffect(() => {
    if (identity) {
      void authenticate(identity);
    }
  }, [identity, authenticate]);

  // Use the actor (works with anonymous or authenticated)
  const fetchData = async () => {
    if (!actor) return;
    try {
      const data = await actor.getData();
      console.log(data);
    } catch (err) {
      console.error("Failed to fetch data:", err);
    }
  };

  return (
    <div>
      {error && <div>Error: {error.message}</div>}
      {status === "initializing" && <div>Initializing...</div>}
      <button onClick={fetchData} disabled={!actor}>Fetch Data</button>
      {isAuthenticated && <span>Authenticated</span>}
    </div>
  );
}

The hook function also exposes non-React helpers that can be used outside components, for example in route guards:

// Wait for the hook to finish initial setup
await useBackendActor.ensureInitialized();
// Inspect helper predicates
if (useBackendActor.isInitializing()) { /* still initializing */ }
if (useBackendActor.isSuccess()) { /* initialized successfully */ }
if (useBackendActor.isError()) { /* initialization failed */ }
// Check authentication helper
if (useBackendActor.isAuthenticated()) { /* identity attached */ }
// Get actor instance (may be undefined if not initialized)
const actor = useBackendActor.getActor();
// Authenticate the hook with an identity
await useBackendActor.authenticate(identity);

Multiple Canisters

Create a hook for each canister:

// actors.ts
export const useBackendOne = createActorHook<BackendOneService>({
  canisterId: backendOneCanisterId,
  idlFactory: backendOneIdlFactory,
});

export const useBackendTwo = createActorHook<BackendTwoService>({
  canisterId: backendTwoCanisterId,
  idlFactory: backendTwoIdlFactory,
});

Authenticate each hook when an identity becomes available (in a component):

function MultiCanisterComponent() {
  const { identity } = useInternetIdentity();
  const backendOne = useBackendOne();
  const backendTwo = useBackendTwo();

  useEffect(() => {
    if (identity) {
      void backendOne.authenticate(identity);
      void backendTwo.authenticate(identity);
    }
  }, [identity, backendOne, backendTwo]);

  // Use the actors...
}

Router integration

When using a routing library (e.g. TanStack Router) you can initialize the Internet Identity library and then ensure and authenticate your actor hooks before a route loads.

Available functions:

  • ensureAllInitialized(): Promise<void> — wait for all registered actor hooks to finish their initial anonymous setup
  • authenticateAll(identity: Identity, filterCanisterIds?: string[]): Promise<void> — authenticate all (or a filtered subset) of registered hooks with the provided identity
  • Per-hook helpers attached to the hook function: useMyActor.ensureInitialized(), useMyActor.authenticate(identity), useMyActor.getActor(), useMyActor.isAuthenticated()

Basic example (TanStack Router):

import { createRoute, redirect } from "@tanstack/react-router";
import { ensureInitialized as ensureIdentityInitialized } from "ic-use-internet-identity";
import { ensureAllInitialized, authenticateAll } from "ic-use-actor";
import { useBackendOne, useBackendTwo } from "./actors";

const dashboardRoute = createRoute({
  getParentRoute: () => rootRoute,
  path: "dashboard",
  beforeLoad: async () => {
    // 1. Ensure the identity library has finished restoring any cached identity
    const identity = await ensureIdentityInitialized();
    if (!identity) {
      throw redirect({ to: "/login" });
    }

    // 2. Wait for actor hooks to initialize (anonymous agents created)
    await ensureAllInitialized();

    // 3. Authenticate all registered actor hooks with the restored identity
    await authenticateAll(identity);

    // Alternatively authenticate specific hooks:
    // await useBackendOne.ensureInitialized();
    // await useBackendOne.authenticate(identity);
    // await useBackendTwo.ensureInitialized();
    // await useBackendTwo.authenticate(identity);
  },
  component: DashboardComponent,
});

Important notes:

  • Always await the Internet Identity initialization first (ic-use-internet-identity.ensureInitialized()).
  • beforeLoad runs once during navigation and does not react to later authentication changes — use a reactive component to observe auth changes at runtime.

Advanced Usage

Interceptors

Add request/response interceptors to proxy and process or log interactions with your canister. Interceptors intercept booth outgoing requests and incoming responses as well as errors.

function MyComponent() {
  const { actor, authenticate, setInterceptors } = useBackendActor();
  const { identity, logout } = useAuthProvider();
  const navigate = useNavigate();

  // Set up interceptors once - they can access React hooks
  useEffect(() => {
    setInterceptors({
      // Called before each request
      onRequest: (data) => {
        console.log(`Calling ${data.methodName}`, data.args);
        // Modify args if needed
        return data.args;
      },

      // Called after successful responses
      onResponse: (data) => {
        console.log(`Response from ${data.methodName}`, data.response);
        // Modify response if needed
        return data.response;
      },

      // Called on request errors (e.g., network issues)
      onRequestError: (data) => {
        console.error(`Request error in ${data.methodName}`, data.error);
        // Transform or handle error
        return data.error;
      },

      // Called on response errors - can access React hooks here!
      onResponseError: (data) => {
        console.error(`Response error in ${data.methodName}`, data.error);

        // Check for expired identity and handle it
        if (data.error.message?.includes("delegation expired")) {
          logout(); // Call React hook function
          navigate('/login'); // Use React Router
        }

        return data.error;
      },
    });
  }, [setInterceptors, logout, navigate]);

  // Authenticate when identity is available
  useEffect(() => {
    if (identity) {
      authenticate(identity);
    }
  }, [identity, authenticate]);

  // ... rest of component
}

Error Handling

The hook provides error state that you can use to handle initialization errors:

function MyComponent() {
  const { actor, error, clearError, authenticate } = useBackendActor();
  const { identity } = useSiweIdentity();

  if (error) {
    return (
      <div>
        <p>Error: {error.message}</p>
        <button onClick={() => {
          clearError();
          if (identity) {
            authenticate(identity);
          }
        }}>
          Retry
        </button>
      </div>
    );
  }

  // ...
}

Custom HTTP Agent Options

Configure the HTTP agent with custom options:

export const useBackendActor = createActorHook<_SERVICE>({
  canisterId,
  idlFactory,
  httpAgentOptions: {
    host: "https://ic0.app",
    credentials: "include",
    headers: {
      "X-Custom-Header": "value",
    },
  },
  actorOptions: {
    callTransform: (methodName, args, callConfig) => {
      // Transform calls before sending
      return [methodName, args, callConfig];
    },
    queryTransform: (methodName, args, callConfig) => {
      // Transform queries before sending
      return [methodName, args, callConfig];
    },
  },
});

API Reference

createActorHook

Creates a React hook for interacting with an IC canister.

function createActorHook<T>(options: CreateActorHookOptions<T>): () => UseActorReturn<T>

Options

| Option | Type | Required | Description | |--------|------|----------|-------------| | canisterId | string | Yes | The canister ID | | idlFactory | IDL.InterfaceFactory | Yes | The IDL factory for the canister | | httpAgentOptions | HttpAgentOptions | No | Options for the HTTP agent | | actorOptions | ActorConfig | No | Options for the actor |

Hook Return Value

The hook returns runtime state and helpers for interacting with the actor:

interface UseActorReturn<T> {
  actor: ActorSubclass<T> | undefined;

  // Initialization status: 'initializing' | 'success' | 'error'
  status: "initializing" | "success" | "error";
  // Convenience booleans derived from `status`
  isInitializing: boolean; // status === 'initializing'
  isSuccess: boolean;      // status === 'success' (actor instance created)
  isError: boolean;        // status === 'error'

  // Authentication flag (separate from initialization)
  isAuthenticated: boolean; // whether an identity has been attached

  // Any error that occurred during initialization. Authentication and interceptor errors do not populate this field.
  error?: Error;

  // Helpers
  authenticate: (identity: Identity) => Promise<void>;
  setInterceptors: (interceptors: InterceptorOptions) => void;
  reset: () => void;
  clearError: () => void;
}

Notes:

  • isSuccess means the actor instance was successfully created (initialization completed). It does NOT imply the actor has been authenticated — use isAuthenticated to check identity attachment.
  • authenticate(identity) attaches the identity to the actor's agent (no network calls) and updates isAuthenticated.

Non-react helpers (attached to the hook function)

Each hook function also exposes helpers you can call outside React (useful for route guards):

  • ensureInitialized(): Promise<ActorSubclass<T> | undefined> — wait for the hook's initial actor initialization to complete.
  • authenticate(identity: Identity): Promise<void> — attach an identity to the actor (same as the hook method).
  • getActor(): ActorSubclass<T> | undefined — get the current actor instance (may be proxied by interceptors).
  • isAuthenticated(): boolean — whether an identity is attached.
  • isInitializing(): boolean — predicate for initialization in progress.
  • isSuccess(): boolean — predicate for initialization success.
  • isError(): boolean — predicate for initialization error.

Example:

await useBackendActor.ensureInitialized();
if (!useBackendActor.isSuccess()) throw new Error('Actor failed to initialize');
if (!useBackendActor.isAuthenticated()) await useBackendActor.authenticate(identity);
const actor = useBackendActor.getActor();

Property summary

| Property | Type | Description | |----------|------|-------------| | actor | ActorSubclass<T> | undefined | The actor instance (initialized with anonymous agent by default) | | status | "initializing" | "success" | "error" | Initialization status of the actor (only initialization) | | isInitializing | boolean | status === "initializing" | | isSuccess | boolean | status === "success" (actor instance created) | | isError | boolean | status === "error" | | isAuthenticated | boolean | Whether an identity has been attached to the actor | | error | Error | undefined | Any error that occurred during initialization. Authentication and interceptor errors do not populate this field. | | authenticate | (identity: Identity) => Promise<void> | Attach an identity to the actor's agent | | setInterceptors | (interceptors: InterceptorOptions) => void | Apply request/response interceptors to the actor | | reset | () => void | Reset the actor state and reinitialize | | clearError | () => void | Clear stored error state |

Global Helpers

Helpers that operate across all registered hook instances (useful when your app creates multiple actor hooks):

  • ensureAllInitialized(): Promise<void> — waits for every registered hook to finish its initial anonymous setup. Useful in route guards where you want all actor hooks ready.

  • authenticateAll(identity: Identity, filterCanisterIds?: string[]): Promise<void> — attaches the provided identity to all registered hooks; if filterCanisterIds is supplied only hooks whose canisterId is included will be authenticated. Throws if any hook's authentication fails.

  • authenticateCanister(identity: Identity, canisterId: string): Promise<void> — convenience wrapper to authenticate hooks for a specific canister id.

Example (router integration):

import { ensureAllInitialized, authenticateAll } from 'ic-use-actor';
import { ensureInitialized as ensureIdentityInitialized } from 'ic-use-internet-identity';

const identity = await ensureIdentityInitialized();
if (!identity) throw redirect('/login');
await ensureAllInitialized();
await authenticateAll(identity);

Notes on global helpers

  • ensureAllInitialized only waits for the initial anonymous HttpAgent + actor creation — it does not authenticate hooks.
  • authenticateAll will call each hook's authenticate helper which updates the per-hook isAuthenticated flag.

Migration from v0.1.x

If you're upgrading from v0.1.x, check out the Migration Guide for detailed instructions on updating your code to use the new API.

Author

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT