@ic-reactor/cli
The IC Reactor CLI (@ic-reactor/cli) helps you generate typed reactor files, optional React hooks, and TypeScript declarations from your Candid files. It’s useful if you’re not using Vite or want more control over the generation process.
It uses the shared @ic-reactor/codegen pipeline, so
its output stays aligned with the Vite plugin.
Installation
Section titled “Installation”Requires Node.js 22.12.0 or later.
bash pnpm add -D @ic-reactor/cli
bash npm install -D @ic-reactor/cli
bash yarn add -D @ic-reactor/cli
bash bun add -D @ic-reactor/cli
Commands
Section titled “Commands”Initialize the configuration file (ic-reactor.json) in the current directory.
npx ic-reactor initOptions:
-y, --yes: Skip prompts and use defaults.-o, --out-dir <path>: Output directory for generated files (default:src/declarations). Only applied together with-y; the interactive flow prompts for it instead.
init writes to the current directory and nowhere else. A config in a parent
directory belongs to a different project and is never modified, so running
init inside a monorepo package creates that package’s own config.
-y is fully non-interactive and safe to run with stdin closed in CI. When
ic-reactor.json already exists in the current directory it is left exactly as
it is; delete it, or run init without -y and confirm the overwrite, to
reconfigure. Cancelling a prompt aborts before anything is written.
generate
Section titled “generate”Regenerates hooks and declarations for all configured canisters. Run this
whenever your .did files change.
npx ic-reactor generateOptions:
-c, --canister <name>: Generate only the canister whose key incanistersis<name>.--clean: Remove generated output for canisters that are no longer configured.--bindgen-only: Generate only the files underdeclarations/.
For each canister, the CLI writes into <outDir>/<canister>/:
declarations/<did-basename>.didcopydeclarations/<did-basename>.d.tsTypeScript service typesdeclarations/<did-basename>.jsIDL factory moduleindex.generated.tsmanaged reactor implementation, with optional typed hook exportsindex.factories.generated.tsmanaged query and mutation factories, only for a canister that setsfactories: trueindex.tsuser-facing entrypoint
Declaration filenames follow the .did file’s basename, not the canister name.
After every run, declarations/ holds only the files that run generated. A
file whose content has not changed is not rewritten, so regenerating from an
unchanged .did and config leaves every file untouched.
If Prettier resolves from the directory holding ic-reactor.json, installed
by that project or by a parent workspace, the CLI formats the .js, .d.ts,
index.generated.ts, index.factories.generated.ts and the index.ts wrapper
it writes with it and your Prettier config. Regenerating then leaves formatted, committed output
unchanged. Without Prettier the declarations hold the Candid parser’s output
followed by a newline.
Because every run brings the output of the canisters it generates up to date,
the only output that can go stale is the directory of a canister that was
renamed or removed from the config. --clean removes those, and only those: it
leaves configured canisters (including the index.ts you own) alone, never
deletes a directory holding no generated file, and is skipped for
--canister <name> runs.
The CLI regenerates index.generated.ts on every run. It creates index.ts
once, then preserves it unless the file is still the default wrapper or an
older generated scaffold that can be migrated automatically.
Set target to choose the generated runtime:
react(default): generates the reactor plus bound React hookscore: generates only the typed reactor exports with no React dependency
Use --bindgen-only when you only want the generated declaration files. In
that mode, the CLI skips index.generated.ts, index.factories.generated.ts
and index.ts entirely and leaves any existing reactor files untouched.
Query and mutation factories
Section titled “Query and mutation factories”Set factories: true on a canister to also generate
index.factories.generated.ts, with one object per method bound to the
generated reactor: createQuery for a query method without arguments,
createQueryFactory for one with arguments, and createMutation for an
update or oneway method. Each is named after its method in camelCase with a
Query or Mutation suffix, so get_message gives getMessageQuery.
{ "canisters": { "backend": { "name": "backend", "didFile": "src/backend/backend.did", "factories": true } }}import { getMessageQuery, setMessageMutation } from "./declarations/backend"
// In a componentfunction Message() { const { data } = getMessageQuery.useQuery()
return <p>{data}</p>}
// Anywhere, outside React includedasync function saveMessage() { await setMessageMutation.execute(["hello"])}The default index.ts wrapper re-exports the file. If you have edited your
index.ts, the CLI leaves it alone and prints a warning until it re-exports
the factories. Removing factories, or setting it to false, removes the file
on the next run. It needs
target: "react". See
Query and Mutation Factories
for the naming rule for any method name and how to customize a factory.
Configuration
Section titled “Configuration”The ic-reactor.json file controls the CLI behavior.
{ "$schema": "https://raw.githubusercontent.com/B3Pay/ic-reactor/main/packages/cli/schema.json", "outDir": "src/declarations", "clientManagerPath": "../../clients", "target": "react", "canisters": { "backend": { "name": "backend", "didFile": "src/backend/backend.did", "canisterId": "rrkah-fqaaa-aaaaa-aaaaq-cai" } }}Set canisterId for any build that is not served from a local replica. It is
written into index.generated.ts. Without it the generated reactor looks its id
up in the ic_env cookie, which a ClientManager trusts only on a local
replica unless it sets allowEnvConfig: true, so importing the generated module
throws canisterId is required for "backend" in Node, during SSR and on a
deployed origin. Leave it out only for local development where an ic_env
cookie carries the id, such as the one the Vite plugin sets.
Options
Section titled “Options”| Key | Type | Description |
|---|---|---|
outDir |
string |
Base output directory for generated files (required). |
canisters |
Record<string, CanisterConfig> |
Canister configurations by key (required). --canister selects one by its key. |
clientManagerPath |
string |
Path to a custom ClientManager instance (optional). |
target |
"react" | "core" |
Default generated runtime target (optional). |
Canister Config
Section titled “Canister Config”| Key | Type | Description |
|---|---|---|
name |
string |
Canister name (required). It names the generated reactor, its hooks and the default output folder <outDir>/<name>, and it need not match the entry’s key in canisters. |
didFile |
string |
Path to the .did file (required). |
mode |
string |
Reactor class to generate: Reactor, DisplayReactor (default), CandidReactor, CandidDisplayReactor, MetadataDisplayReactor. |
outDir |
string |
Override output directory for this canister. |
clientManagerPath |
string |
Override client manager path. |
target |
string |
Override generated runtime target. |
canisterId |
string |
Canister ID written into index.generated.ts. Needed for any build not served from a local replica; see above. |
factories |
boolean |
Also generate index.factories.generated.ts, a query or mutation object per method (default false). Needs target: "react"; see above. |
Examples
Section titled “Examples”- Codegen in Action: Comparison between CLI and Vite plugin generated code.
- TypeScript Demo: Using the CLI to generate pure TypeScript reactors without React.
See Also
Section titled “See Also”- @ic-reactor/codegen — Shared generation pipeline
- @ic-reactor/vite-plugin — Automatic generation during Vite development