Skip to content

Quick Start

This tutorial walks you through creating your first IC Reactor integration with React. By the end, you’ll have a fully functional data-fetching setup with queries and mutations.

  1. Terminal window
    pnpm add @ic-reactor/react @icp-sdk/core @tanstack/react-query
  2. Create a central configuration file for your canister connection:

    src/reactor/index.ts
    import { ClientManager, Reactor } from "@ic-reactor/react"
    import { QueryClient } from "@tanstack/react-query"
    import { idlFactory, type _SERVICE } from "../declarations/backend"
    // Create QueryClient with sensible defaults
    export const queryClient = new QueryClient({
    defaultOptions: {
    queries: {
    staleTime: 1000 * 60, // 1 minute
    gcTime: 1000 * 60 * 5, // 5 minutes
    },
    },
    })
    // Create ClientManager (handles agent and auth)
    export const clientManager = new ClientManager({
    queryClient,
    withProcessEnv: true, // Auto-detect local vs IC network
    })
    // Create Reactor for your canister
    export const backend = new Reactor<_SERVICE>({
    clientManager,
    idlFactory,
    canisterId: import.meta.env.VITE_BACKEND_CANISTER_ID,
    })
  3. Create reusable hooks from your Reactor:

    src/reactor/hooks.ts
    import { createActorHooks } from "@ic-reactor/react"
    import { backend } from "./index"
    export const { useActorQuery, useActorMutation } = createActorHooks(backend)
  4. Use useActorQuery to fetch data from your canister:

    src/components/Greeting.tsx
    import { useActorQuery } from "../reactor/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>
    }
  5. Use useActorMutation to call update methods:

    src/components/Counter.tsx
    import { useState } from "react"
    import { useActorQuery, useActorMutation } from "../reactor/hooks"
    import { backend } from "../reactor"
    function Counter() {
    // Query the current count
    const { data: count } = useActorQuery({
    functionName: "getCount",
    })
    // Mutation to increment
    const { mutate, isPending } = useActorMutation({
    functionName: "increment",
    // Invalidate count after mutation
    invalidateQueries: [
    backend.generateQueryKey({ functionName: "getCount" }),
    ],
    })
    return (
    <div>
    <p>Count: {count ?? "..."}</p>
    <button onClick={() => mutate([])} disabled={isPending}>
    {isPending ? "Incrementing..." : "Increment"}
    </button>
    </div>
    )
    }

Set up your canister IDs in your environment file:

Terminal window
# .env.local (for Vite)
VITE_BACKEND_CANISTER_ID=rrkah-fqaaa-aaaaa-aaaaq-cai
# Or if using CRA
REACT_APP_BACKEND_CANISTER_ID=rrkah-fqaaa-aaaaa-aaaaq-cai

You now have IC Reactor working! Here’s what to explore next: