Skip to content
IC Reactor

Quick Start

This tutorial walks you through creating your first IC Reactor integration with React. We’ll use the Vite Plugin to automate declaration generation and hook creation, which is the recommended way to use IC Reactor.

  1. Terminal window
    pnpm add @ic-reactor/react @icp-sdk/core @tanstack/react-query
    pnpm add -D @ic-reactor/vite-plugin
  2. Add the plugin to your vite.config.ts. This will automatically generate TypeScript declarations and React hooks whenever you modify your .did file.

    vite.config.ts
    import { icReactor } from "@ic-reactor/vite-plugin"
    export default defineConfig({
    plugins: [
    icReactor({
    canisters: [
    {
    name: "backend",
    didFile: "src/backend/backend.did",
    },
    ],
    }),
    ],
    })
  3. Create a central ClientManager. The generated reactors will use this by default.

    src/clients.ts
    import { ClientManager } from "@ic-reactor/react"
    import { QueryClient } from "@tanstack/react-query"
    export const queryClient = new QueryClient()
    export const clientManager = new ClientManager({ queryClient })
  4. Import the generated hooks directly from your declarations folder. They are fully typed and ready to use!

    src/components/Greeting.tsx
    import { useBackendQuery } from "../declarations/backend"
    function Greeting() {
    const { data, isPending, error } = useBackendQuery({
    functionName: "greet",
    args: ["World"],
    })
    if (isPending) return <div>Loading...</div>
    if (error) return <div>Error: {error.message}</div>
    return <h1>{data}</h1>
    }
  5. Mutations are also generated automatically:

    src/components/Counter.tsx
    import { useBackendQuery, useBackendMutation } from "../declarations/backend"
    function Counter() {
    const { data: count } = useBackendQuery({ functionName: "getCount" })
    const { mutate, isPending } = useBackendMutation({
    functionName: "increment",
    onSuccess: () => {
    // The generated reactor provides helper to invalidate queries
    // ... custom invalidation logic here
    },
    })
    return (
    <div>
    <p>Count: {count ?? "..."}</p>
    <button onClick={() => mutate([])} disabled={isPending}>
    {isPending ? "Incrementing..." : "Increment"}
    </button>
    </div>
    )
    }

Using the Vite plugin provides several advantages out-of-the-box:

  • Zero-config Proxy: /api requests are automatically proxied to your local replica.
  • Auto-Canister IDs: Canister IDs are automatically detected from your local network through icp-cli when environment injection is enabled.
  • Identity Support: Works seamlessly with local Internet Identity setups.

In development the plugin injects local canister IDs through the ic_env cookie, and the generated reactor resolves its own ID from that cookie using the key PUBLIC_CANISTER_ID:backend.

For production, pin the ID on the canister entry in vite.config.ts. The generated reactor then emits it directly and no cookie or environment variable is involved:

icReactor({
canisters: [
{
name: "backend",
didFile: "src/backend/backend.did",
canisterId: "rrkah-fqaaa-aaaaa-aaaaq-cai",
},
],
})

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