Skip to content

React hooks ​

@civitai/blocks-react is the React-first way to build a Civitai App. Most hooks wrap a slice of the message bridge so you never touch postMessage directly — you call a hook, get typed state back, and the host brokers the privileged work. A few call a REST route themselves; the Trust model box below names them.

The signatures on the hook pages are generated from the published package's type definitions; the examples, and everything each README section says after its example, come from its README.

Which transport for what

See Concepts → Transport models.

Most hooks on this page are bridge hooks, and most of the data they carry also has a REST route a block can call with the token it already holds. The porting guide maps every hook the package exports to its route, or says why it stays on the bridge.

This page is generated from @civitai/blocks-react's own type definitions and README, so it describes each hook as the package ships it. That is a property of the generator, not a recommendation of one transport over the other.

Trust model

The hooks fall into three classes, and the credential is the boundary in all of them.

  • Bridge hooks — most of this page. The hook posts a message; the host resolves the viewer from the block token and performs the privileged call on Civitai's side of the iframe boundary, re-checking scopes each time.
  • Direct-fetch hooks — useTip, useTipAllowance, useEntitlements, useGoodPurchase and useGenerationResources. These are not host-mediated. The hook calls fetch() on a /api/v1/blocks/* route at the validated host origin, sending the block token your block holds as a bearer. The server verifies that token on every request and, where the route declares one, checks its required scope (social:tip:self for the two tip hooks, goods:purchase:self and goods:read:self for the goods hooks; generation-resources takes any valid block token). An expired token is a 401 here, and a mock host that only answers messages never sees these calls.
  • Local hooks. A few read state the block already has or work entirely inside the frame, and call nothing on Civitai.

Calling a block route yourself has the same trust model as the direct-fetch hooks: your block holds a short-lived scoped credential, and the server re-checks it and its scopes on every request.

Not every capability is a hook: generation has five body kinds

A search of this page for a hook name will not find everything the platform can generate. One hook, useBuzzWorkflow, takes every kind of generation body, and what it generates is chosen by the kind of the body you pass it, not by a separate hook. Its entry is generated from the package README, whose example sends only kind: 'textToImage'. The five kinds:

Body kindWhat it runsRead
'textToImage'An image from a prompt, with LoRAs and source imagesGenerating images
'customComfy'A ComfyUI workflow: a registered recipe, or your own graphComfy on Civitai
'step'One orchestrator step: a registered step id, or any step $type passed throughWhat the bridge can and cannot do
'training'A LoRA trained on the viewer's own imagesWorkflowBodyTraining
'workflow'Up to eight steps as one generation, a later step reading an earlier one's resultMulti-step workflows

See Workflow bodies: the kind union below before concluding a capability is missing.

Hooks by page ​

The hooks are documented across the pages below, grouped by what they do, so that each page's .md copy stays small enough for an agent's fetch tool to read whole. Every hook the package exports is listed here by name; a hook you cannot find in this index is not in the pinned package.

Context, frame and analytics hooks

The host handshake, theme, sizing, the block token and settings, direct-load and nested documents, and analytics.

Generation hooks

useBuzzWorkflow and the hooks that list, publish and resource a generation.

Batch, confirmed-step and training hooks

Estimating and running batches, running one job above the per-call budget, and LoRA training.

Buzz, purchase and tip hooks

Buzz balance and transactions, Buzz and goods purchases, entitlements, compensation and tips.

Viewer, consent and navigation hooks

The viewer, sign-in and consent prompts, maturity and gated images, following collections, and navigating the host.

Storage, picker and media hooks

Per-app and shared storage, wildcard packs, checkpoint, resource and image pickers, uploading and saving images, and creating posts.

Workflow bodies: the kind union ​

estimate() and submit() both take a full WorkflowBody — a discriminated union keyed by kind. The hook forwards the body to the host verbatim and never reads member-specific fields, so every member except training flows through the same estimate → submit → watch lifecycle useBuzzWorkflow documents. training is quoted with estimate() but run with useRunTraining(), and submit() refuses it.

As of the pinned @civitai/app-sdk@0.71.0 the union has five kind values, and kind: 'step' is itself two arms — six members in all:

kindwhat it runswhat your block sends
textToImagea Civitai checkpoint (plus optional LoRAs / img2img)modelId + modelVersionId + params
customComfya ComfyUI workflow — a server-registered recipe, or your own grapha registered recipe id, or mode: 'inline' plus the graph itself
step (step present)a server-registered orchestrator step (convert-image, chat-completion)a registered step id + bounded params
step (step omitted)an orchestrator step type named directly, input forwarded unmodifieda $type + input + a maxBuzz amount — a reservation, not a spend ceiling
trainingan ai-toolkit LoRA training run on a dataset of the viewer's own imagesa datasetId from usePrepareTrainingDataset() + a base-model key + params; the quoteId from the estimate on the run
workflowup to 8 orchestrator steps as one generation, a later step reading an earlier one's resultsteps (each a $type + input, where any value may be a { $ref, path } reference) + optional arguments; no maxBuzz. See Multi-step workflows

training is a page-app flow behind a host flag that ships off; its availability, money rules and the three hooks it uses are under usePrepareTrainingDataset.

Narrow on body.kind before touching member-specific fields — and note that kind === 'step' alone leaves both step arms in play, so narrow further on whether step is present. The full field tables are in the generation bridge reference.

kind: 'customComfy' — ComfyUI from a block ​

customComfy has two arms, selected by mode. This is the member most often missed, because the generated useBuzzWorkflow example never shows it.

Recipe arm — mode omitted (or 'recipe'). Your block names a server-registered, code-reviewed workflow and passes bounded params; the server owns the graph:

ts
import type { WorkflowBodyCustomComfyRecipe } from '@civitai/app-sdk/blocks';

const body: WorkflowBodyCustomComfyRecipe = {
  kind: 'customComfy',
  recipe: 'starter-comfy-txt2img', // a SERVER-registered id — unknown ids are rejected fail-closed
  params: {
    prompt: 'a serene alpine lake at golden hour',
    // seed?: number | null — omit to let the orchestrator pick
  },
};

Inline arm — mode: 'inline' (required). Your block ships the ComfyUI graph itself, plus a declared resources manifest and a maxBuzz ceiling:

ts
import type { WorkflowBodyCustomComfyInline } from '@civitai/app-sdk/blocks';

const body: WorkflowBodyCustomComfyInline = {
  kind: 'customComfy',
  mode: 'inline', // REQUIRED, and exactly this value — see below
  workflow: {
    // the ComfyUI `/prompt` graph, keyed by node id — the shape
    // ComfyUI's "Save (API Format)" export produces
    '3': { class_type: 'KSampler', inputs: { seed: 42, steps: 20 } },
  },
  resources: ['urn:air:sdxl:checkpoint:civitai:101055@128078'],
  maxBuzz: 50, // integer 1…250, and ALSO the step timeout in seconds
};

Three things trip up a first attempt, all covered in the guide:

  • mode: 'inline' is required. Including a workflow key does not select the arm — a body without mode routes to the recipe arm and is then rejected for a missing recipe.
  • resources is a declared manifest, not an inference. Every AIR the graph names must also appear in resources or the submit is rejected.
  • maxBuzz is the only spend knob, and doubles as the step timeout in seconds.

The published SDK types BOTH arms

WorkflowBodyCustomComfy is itself a union on mode, and both arms are importable from @civitai/app-sdk/blocks: WorkflowBodyCustomComfyRecipe and WorkflowBodyCustomComfyInline (plus InlineComfyNode for the graph nodes). Import them rather than declaring the inline shape locally — a hand-declared copy will drift from the SDK.

Annotate the ARM, not the union, as both examples above do. When the mode discriminant is omitted — which is the shape this page recommends — TypeScript's excess-property check runs against the whole union and accepts any key belonging to any constituent, so a body annotated WorkflowBodyCustomComfy silently tolerates a workflow key on a recipe body and you find out at submit, server-side. (Spelling mode out explicitly narrows the union to one constituent and does restore the error — but then you are carrying a field the recommended shape leaves off.) Annotating WorkflowBodyCustomComfyRecipe (or …Inline) makes it a compile error either way. Use the union only where a value genuinely holds either arm.

Still narrow on the value of body.mode === 'inline', never on whether the key is present: mode is optional on the recipe arm, so a body that merely carries a workflow key routes to the recipe arm and is rejected for a missing recipe.

Both arms are mod-gated and page-token-only — a model-bound token is rejected before either arm is inspected. For the graph rules, the entitlement and moderation gates, the budget model, and a runnable local example, read Comfy on Civitai (customComfy) — this section is a pointer, not a replacement.

Install ​

bash
pnpm add @civitai/blocks-react @civitai/app-sdk

See the Quickstart for a full scaffold, and the message bridge reference for the protocol these hooks sit on.

Civitai Developer Documentation