Skip to main content
configurator.tsx renders the configuration UI inside the workflow editor when a workspace member adds or edits a block. It runs client-side only, with no server access or side-effects. Every block needs a configurator.tsx. It has two responsibilities:
  1. Render inputs for each field in the block’s config schema so workspace members can fill them in.
  2. Declare outcomes via the Outcome component so the editor knows what data each outcome branch exposes to downstream steps.

defineConfigurator

Call defineConfigurator in configurator.tsx, passing the block and a render function. The render function receives the block definition and returns the JSX form.
configurator.tsx

useConfigurator

Call Workflows.useConfigurator(workflowBlock) inside the render function to get typed input components and the Outcome component bound to the block’s config schema. Pass the workflowBlock from the defineConfigurator render callback argument.

Input components

Each input component corresponds to a schema field type. The name prop is type-checked against the schema. TypeScript will error if name doesn’t match a field of the correct type. All input components accept at minimum:

Combobox options

A ComboboxInput on a ConfigSchema.string() field needs an options prop that supplies the choices. Pass either a static array of {value, label} options:
Or an options provider that loads them from a third party as the member types. A provider is an object with search (returns options matching a query) and getOption (resolves a single saved value back to its label). Both call server functions, since the configurator cannot fetch directly.
project-options.ts
See <Combobox /> for the full options-provider reference.

Outcome component

Every {type: "outcome"} return value carries an id, a short identifier you choose (e.g. "created", "not-found"). Attio exposes each unique id as a named branch in the workflow editor. Workspace members wire each branch to a different downstream step. Return different id values for different code paths to build multi-branch flows. The Outcome component declares each id to the editor and describes what data that branch carries. The editor uses this to expose typed variables to downstream steps.
When the block has only one outcome, label is optional; the editor uses the block’s title as the branch label. When the block has multiple outcomes, always provide label so workspace members can tell the branches apart.
The schema prop uses Workflows.OutcomeSchema.* constructors, a separate namespace from the config schema constructors. Available types mirror the Outcome schema node types.

Watching config values

watch reads the current value of a config field as the workspace member types in the editor. Use it to conditionally show or hide other inputs based on what has been filled in. watch returns a discriminated union; always check type before reading value. value is fully typed — on a ConfigSchema.stringEnum(["basic", "advanced"]) field, modeConfig.value is "basic" | "advanced", not string. On trigger blocks, type can only ever be "static": dynamic variable references come from previous steps, and a trigger has none.
type: "dynamic" means the field will be resolved at runtime, not in the editor. You cannot read a concrete value from it. When a field is dynamic, either show all dependent inputs (safe default) or hide inputs that require a known value.

Loading data

Configurators often need data from a third-party service — the list of projects to pick from, the fields on a selected record, and so on. Declare your Outcomes up front, independent of anything you load, and let the block suspend while data is loading: the editor keeps the block in a loading state until it stops suspending, then reads its outcomes.

Prefer an options provider for dropdowns

Load combobox choices with an options provider. The provider fetches options lazily as the member searches, so the block renders right away and your outcomes stay fixed.
configurator.tsx

Loading other data with useAsyncCache

When you need data that isn’t a set of combobox options — for example, to label or shape the inputs you render — load it with useAsyncCache. It suspends while loading, so the editor keeps the block in its loading state until the data is ready; you don’t need to add your own loading indicator. Use a fixed cache key so it loads once when the configurator opens.
configurator.tsx

Example

A complete step configurator with two outcomes and typed data:
configurator.tsx

See also