API Reference
Complete API reference for all RilayKit packages.
API Reference
Complete API documentation for all RilayKit packages.
@rilaykit/core
The core package provides the component registry, validation system, conditional logic, and error classes shared across all RilayKit packages.
ril
The central configuration class. All RilayKit functionality starts with a ril instance. Every mutation method returns a new instance (immutable API).
ril.create()
Creates a new RilayKit configuration instance.
import { ril } from '@rilaykit/core';
const rilay = ril.create();Returns: ril<Record<string, never>>
.addComponent(type, config)
Registers a component renderer. Returns a new instance with the component added.
const rilay = ril.create()
.addComponent('input', {
name: 'Text Input',
renderer: InputRenderer,
defaultProps: { placeholder: 'Enter text...' },
})
.addComponent('email', {
name: 'Email Input',
renderer: EmailRenderer,
validation: {
validate: email(),
validateOnBlur: true,
},
});Parameters:
type: string-- Unique component type identifierconfig: Omit<ComponentConfig<T>, 'id' | 'type'>-- Component configuration
Returns: ril<C & { [type]: Props }> (new instance)
.configure(config)
Configures form and workflow renderers via a single method. Returns a new instance.
const rilay = ril.create()
.addComponent('input', { name: 'Input', renderer: InputRenderer })
.configure({
rowRenderer: CustomRowRenderer,
bodyRenderer: CustomBodyRenderer,
submitButtonRenderer: CustomSubmitButton,
fieldRenderer: CustomFieldRenderer,
stepperRenderer: CustomStepper,
nextButtonRenderer: CustomNextButton,
previousButtonRenderer: CustomPreviousButton,
skipButtonRenderer: CustomSkipButton,
});Accepted keys:
| Form renderers | Workflow renderers |
|---|---|
rowRenderer | stepperRenderer |
bodyRenderer | nextButtonRenderer |
submitButtonRenderer | previousButtonRenderer |
fieldRenderer | skipButtonRenderer |
Returns: ril<C> (new instance)
.getComponent(id)
Retrieves a registered component configuration.
const config = rilay.getComponent('input');Returns: ComponentConfig<T> | undefined
.getAllComponents()
Returns all registered component configurations.
Returns: ComponentConfig[]
.hasComponent(id)
Checks whether a component is registered.
Returns: boolean
.removeComponent(id)
Returns a new instance without the specified component.
Returns: ril<C> (new instance)
.clear()
Returns a new instance with all components removed. Render configurations are preserved.
Returns: ril<C> (new instance)
.clone()
Creates a deep copy of the current instance.
Returns: ril<C> (new instance)
.getFormRenderConfig()
Returns the current form render configuration.
Returns: FormRenderConfig
.getWorkflowRenderConfig()
Returns the current workflow render configuration.
Returns: WorkflowRenderConfig
.validate()
Synchronously validates the configuration (duplicate IDs, missing renderers, invalid keys).
Returns: string[] -- Array of error messages (empty if valid)
.validateAsync()
Asynchronous validation with structured error handling. Throws ValidationError if invalid.
Returns: Promise<AsyncValidationResult>
interface AsyncValidationResult {
isValid: boolean;
errors: string[];
warnings?: string[];
}.getStats()
Returns statistics about the current configuration.
const stats = rilay.getStats();
// {
// total: number;
// byType: Record<string, number>;
// hasCustomRenderers: {
// row: boolean;
// body: boolean;
// submitButton: boolean;
// field: boolean;
// stepper: boolean;
// workflowNextButton: boolean;
// workflowPreviousButton: boolean;
// workflowSkipButton: boolean;
// };
// }ComponentRenderProps
Props passed to every component renderer function.
interface ComponentRenderProps<T = any> {
id: string;
props: T;
value?: any;
onChange?: (value: any) => void;
onBlur?: () => void;
disabled?: boolean;
error?: ValidationError[];
isValidating?: boolean;
[key: string]: any;
}
type ComponentRenderer<T = any> = (
props: ComponentRenderProps<T>
) => React.ReactElement;Validators
All built-in validators implement the Standard Schema interface (StandardSchemaV1). They can be used alongside Zod, Yup, or any other Standard Schema-compatible library.
import {
required, email, url, minLength, maxLength,
pattern, number, min, max, custom, async, combine,
} from '@rilaykit/core';| Validator | Signature | Description |
|---|---|---|
required | required(msg?) | Field must have a value |
email | email(msg?) | Valid email format |
url | url(msg?) | Valid URL |
minLength | minLength(min, msg?) | Minimum string length |
maxLength | maxLength(max, msg?) | Maximum string length |
pattern | pattern(regex, msg?) | Matches regular expression |
number | number(msg?) | Must be a valid number |
min | min(val, msg?) | Minimum numeric value |
max | max(val, msg?) | Maximum numeric value |
custom | custom<T>(fn, msg?) | Synchronous custom validation (fn: (value: T) => boolean) |
async | async<T>(fn, msg?) | Asynchronous custom validation (fn: (value: T) => Promise<boolean>) |
combine | combine<T>(...schemas) | Combines multiple StandardSchemaV1 schemas into one |
All validators return StandardSchemaV1, so you can mix them freely with Zod, Yup, or any compatible library.
Conditional Logic
when(field)
Creates a ConditionBuilder for declaring conditional behaviors on fields or steps.
import { when } from '@rilaykit/core';
const condition = when('accountType').equals('business');Returns: ConditionBuilder
ConditionBuilder Methods
Comparison operators:
| Method | Signature | Description |
|---|---|---|
.equals() | .equals(value: ConditionValue) | Strict equality |
.notEquals() | .notEquals(value: ConditionValue) | Strict inequality |
.greaterThan() | .greaterThan(value: number) | Numeric greater than |
.lessThan() | .lessThan(value: number) | Numeric less than |
.greaterThanOrEqual() | .greaterThanOrEqual(value: number) | Numeric greater than or equal |
.lessThanOrEqual() | .lessThanOrEqual(value: number) | Numeric less than or equal |
.contains() | .contains(value: string) | String/array contains |
.notContains() | .notContains(value: string) | String/array does not contain |
.in() | .in(values: Array) | Value is in array |
.notIn() | .notIn(values: Array) | Value is not in array |
.matches() | .matches(pattern: string | RegExp) | Matches regex pattern |
.exists() | .exists() | Value is not null/undefined |
.notExists() | .notExists() | Value is null/undefined |
Logical combinators:
| Method | Signature | Description |
|---|---|---|
.and() | .and(condition: ConditionBuilder | ConditionConfig) | Logical AND |
.or() | .or(condition: ConditionBuilder | ConditionConfig) | Logical OR |
Terminal methods:
| Method | Signature | Description |
|---|---|---|
.build() | .build() | Returns the serializable ConditionConfig |
.evaluate() | .evaluate(data: Record<string, any>) | Evaluates the condition against data |
// Boolean check
when('isVerified').equals(true)
// Combining conditions
when('role').equals('admin')
.or(when('permissions').contains('write'))
// Nested field paths
when('address.country').equals('FR')Field Effects
onChange(fieldId, handler)
Creates a declarative field effect that reacts to value changes. Place it in a field's effects array to define reactive logic without useEffect.
import { onChange } from '@rilaykit/core';
onChange('country', async (value, { setValue, setProps }) => {
setValue('city', '');
setProps('city', { options: await fetchCities(value) });
});Parameters:
fieldId: string-- The field ID to watch for changeshandler: FieldEffectHandler-- Callback invoked when the watched field's value changes
Returns: FieldEffect
The fieldId in onChange() is the field being watched, not the field the effect is declared on. This lets you place an effect on total that watches price.
FieldEffect Types
interface FieldEffectContext {
readonly setValue: (fieldId: string, value: unknown) => void;
readonly setProps: (fieldId: string, props: Record<string, unknown>) => void;
readonly getValues: () => Record<string, unknown>;
readonly getFieldValue: (fieldId: string) => unknown;
}
type FieldEffectHandler = (
newValue: unknown,
context: FieldEffectContext
) => void | Promise<void>;
interface FieldEffect {
readonly trigger: 'change';
readonly watchFieldId: string;
readonly handler: FieldEffectHandler;
}
type FieldEffects = readonly FieldEffect[];| Context method | Description |
|---|---|
setValue(fieldId, value) | Set another field's value (triggers cascading effects) |
setProps(fieldId, props) | Merge dynamic props into a field (e.g., { options: [...] }) |
getValues() | Get all current form values |
getFieldValue(fieldId) | Get a specific field's current value |
Protections built into the EffectEngine:
- Cascade depth limit — max 10 levels of chained effects before warning and stopping
- Cycle detection — warns if a field triggers itself through a circular chain
- Async abort — rapid changes abort previous async handlers; only the latest takes effect
Error Classes
import { RilayError, ValidationError, DuplicateIdError } from '@rilaykit/core';| Class | Code | Description |
|---|---|---|
RilayError | Custom code | Base error class. Properties: code: string, meta?: Record<string, any> |
ValidationError | VALIDATION_ERROR | Thrown on validation failures |
DuplicateIdError | DUPLICATE_ID_ERROR | Thrown on duplicate ID registration |
@rilaykit/forms
The forms package provides components, a builder, and granular Zustand-backed hooks for building type-safe forms.
Components
Form
Top-level form wrapper. Accepts either a built FormConfiguration or a form builder (auto-builds internally).
import { Form } from '@rilaykit/forms';
<Form
formConfig={myForm}
onSubmit={handleSubmit}
defaultValues={{ name: '', email: '' }}
onFieldChange={(fieldId, value, formData) => {}}
className="my-form"
>
{children}
</Form>| Prop | Type | Description |
|---|---|---|
formConfig | FormConfiguration | form | Form configuration or builder instance |
defaultValues? | Record<string, any> | Initial form values |
onSubmit? | (data: Record<string, any>) => void | Promise<void> | Submission handler |
onFieldChange? | (fieldId: string, value: any, formData: Record<string, any>) => void | Field change callback |
className? | string | CSS class for the <form> element |
children | React.ReactNode | Form content |
FormProvider
Lower-level provider that wraps the form context. Form delegates to this internally.
| Prop | Type | Description |
|---|---|---|
formConfig | FormConfiguration | Built form configuration |
defaultValues? | Record<string, unknown> | Initial form values |
onSubmit? | (data: Record<string, unknown>) => void | Promise<void> | Submission handler |
onFieldChange? | (fieldId: string, value: unknown, formData: Record<string, unknown>) => void | Field change callback |
className? | string | CSS class |
children | React.ReactNode | Children |
FormBody
Renders all form rows automatically from the form configuration.
<Form formConfig={myForm} onSubmit={handleSubmit}>
<FormBody />
</Form>| Prop | Type | Description |
|---|---|---|
className? | string | CSS class |
FormField
Renders a single form field by ID.
<FormField fieldId="email" disabled={false} className="mb-4" />| Prop | Type | Description |
|---|---|---|
fieldId | string | Field ID (must match a field in the form configuration) |
disabled? | boolean | Force disabled state |
customProps? | Record<string, unknown> | Additional props merged into the component |
className? | string | CSS class for the field wrapper |
forceVisible? | boolean | Override condition-based visibility |
FormRow
Renders a horizontal row of form fields.
| Prop | Type | Description |
|---|---|---|
row | FormFieldRow | Row configuration object |
className? | string | CSS class |
FormSubmitButton
Submit button with automatic loading state from the form store.
| Prop | Type | Description |
|---|---|---|
isSubmitting? | boolean | Override the computed submitting state |
className? | string | CSS class |
form (FormBuilder)
Builder class for creating type-safe form configurations.
Creating a form builder
import { form } from '@rilaykit/forms';
const myForm = form.create(rilConfig, 'contact-form');import { form } from '@rilaykit/forms';
const myForm = new form(rilConfig, 'contact-form');Parameters:
config: ril<C>-- The ril configuration containing component definitionsformId?: string-- Optional unique form identifier (auto-generated if omitted)
.add(...fields) / .add([fields])
Adds fields to the form. Supports multiple calling patterns:
// Single field -- own row
builder.add({ id: 'name', type: 'input', props: { label: 'Name' } });
// Multiple fields -- same row
builder.add(
{ id: 'firstName', type: 'input', props: { label: 'First' } },
{ id: 'lastName', type: 'input', props: { label: 'Last' } },
);
// Array syntax -- explicit single row
builder.add([
{ id: 'email', type: 'email', props: { label: 'Email' } },
{ id: 'phone', type: 'phone', props: { label: 'Phone' } },
]);
// Field with effects
builder.add({
id: 'country',
type: 'select',
props: { label: 'Country' },
effects: [
onChange('country', async (value, { setValue, setProps }) => {
setValue('city', '');
setProps('city', { options: await fetchCities(value) });
})
],
});
// Many fields -- all in one row
builder.add(field1, field2, field3, field4);Returns: this (chainable)
.addSeparateRows(fields)
Adds an array of fields, each on its own row.
builder.addSeparateRows([
{ type: 'input', props: { label: 'Field 1' } },
{ type: 'input', props: { label: 'Field 2' } },
]);Returns: this (chainable)
.setId(id)
Sets or overrides the form identifier.
Returns: this (chainable)
.setValidation(config)
Sets form-level validation configuration.
builder.setValidation({
validate: z.object({
password: z.string().min(8),
confirmPassword: z.string(),
}).refine(data => data.password === data.confirmPassword, {
message: "Passwords don't match",
path: ['confirmPassword'],
}),
validateOnSubmit: true,
});Parameters:
config: FormValidationConfig-- Form-level validation settings
Returns: this (chainable)
.addFieldConditions(fieldId, conditions)
Adds conditional behavior to a field after creation.
builder.addFieldConditions('phone', {
visible: when('contactMethod').equals('phone').build(),
required: when('contactMethod').equals('phone').build(),
});Parameters:
fieldId: string-- Field to add conditions toconditions: ConditionalBehavior-- Condition configuration (visible?,disabled?,required?,readonly?)
Returns: this (chainable)
.updateField(fieldId, updates)
Updates an existing field's configuration.
builder.updateField('email', {
props: { placeholder: 'Enter your email' },
});Returns: this (chainable)
.removeField(fieldId)
Removes a field and cleans up empty rows.
Returns: this (chainable)
.getField(fieldId)
Retrieves a field configuration by ID.
Returns: FormFieldConfig | undefined
.getFields()
Returns all fields as a flat array.
Returns: FormFieldConfig[]
.getRows()
Returns a copy of all form rows.
Returns: FormFieldRow[]
.clear()
Removes all fields and rows, resets the ID generator.
Returns: this (chainable)
.clone(newId?)
Creates a deep copy of the form builder.
Returns: form<C> (new instance)
.validate()
Checks for structural issues (duplicate IDs, missing components, row constraints).
Returns: string[]
.build()
Builds the final FormConfiguration. Throws if validation fails.
Returns: FormConfiguration<C>
.toJSON() / .fromJSON(json)
Serialization and deserialization of the form structure.
.getStats()
Returns form statistics.
const stats = builder.getStats();
// { totalFields, totalRows, averageFieldsPerRow, maxFieldsInRow, minFieldsInRow }Hooks
All form hooks are backed by a Zustand store for granular re-renders.
Field-level hooks re-render only when their specific field's data changes, not on every form update.
Field Hooks
| Hook | Signature | Returns |
|---|---|---|
useFieldValue | useFieldValue<T>(fieldId: string) | T |
useFieldErrors | useFieldErrors(fieldId: string) | ValidationError[] |
useFieldTouched | useFieldTouched(fieldId: string) | boolean |
useFieldValidationState | useFieldValidationState(fieldId: string) | ValidationState |
useFieldConditions | useFieldConditions(fieldId: string) | FieldConditions |
useFieldState | useFieldState(fieldId: string) | FieldState |
useFieldProps | useFieldProps(fieldId: string) | Record<string, unknown> |
useFieldActions | useFieldActions(fieldId: string) | UseFieldActionsResult |
interface FieldState {
value: unknown;
errors: ValidationError[];
validationState: ValidationState; // 'idle' | 'validating' | 'valid' | 'invalid'
touched: boolean;
dirty: boolean;
}
interface FieldConditions {
visible: boolean;
disabled: boolean;
required: boolean;
readonly: boolean;
}
interface UseFieldActionsResult {
setValue: (value: unknown) => void;
setTouched: () => void;
setErrors: (errors: ValidationError[]) => void;
clearErrors: () => void;
setValidationState: (state: ValidationState) => void;
}Form Hooks
| Hook | Signature | Returns |
|---|---|---|
useFormSubmitting | useFormSubmitting() | boolean |
useFormValid | useFormValid() | boolean |
useFormDirty | useFormDirty() | boolean |
useFormValues | useFormValues() | Record<string, unknown> |
useFormSubmitState | useFormSubmitState() | { isSubmitting, isValid, isDirty } |
useFormActions | useFormActions() | UseFormActionsResult |
interface UseFormActionsResult {
setValue: (fieldId: string, value: unknown) => void;
setTouched: (fieldId: string) => void;
setErrors: (fieldId: string, errors: ValidationError[]) => void;
setSubmitting: (isSubmitting: boolean) => void;
reset: (values?: Record<string, unknown>) => void;
setFieldConditions: (fieldId: string, conditions: FieldConditions) => void;
}Context Hook
| Hook | Signature | Returns |
|---|---|---|
useFormConfigContext | useFormConfigContext() | FormConfigContextValue |
interface FormConfigContextValue {
formConfig: FormConfiguration;
conditionsHelpers: { hasConditionalFields, getFieldCondition, isFieldVisible, isFieldDisabled, isFieldRequired, isFieldReadonly };
validateField: (fieldId: string, value?: unknown) => Promise<ValidationResult>;
validateForm: () => Promise<ValidationResult>;
submit: (event?: React.FormEvent) => Promise<boolean>;
}Server-Driven Forms (fromSchema)
Convert JSON schemas into fully functional FormConfiguration objects — with validation, conditions, effects, and repeatables.
fromSchema(schema, config, registry?)
Converts a FormSchema into a FormSchemaResult containing the built FormConfiguration and optional default values.
import { fromSchema } from '@rilaykit/forms';
import type { FormSchema, SchemaRegistry } from '@rilaykit/forms';
const { formConfig, defaultValues } = fromSchema(schema, rilConfig, registry);Parameters:
schema: FormSchema-- The JSON schema definitionconfig: ril<C>-- Your ril configuration with registered componentsregistry?: SchemaRegistry-- Custom validators and effect handlers
Returns: FormSchemaResult<C>
interface FormSchemaResult<C extends Record<string, any>> {
readonly formConfig: FormConfiguration<C>;
readonly defaultValues?: Record<string, unknown>;
}validateSchema(schema, config, registry?)
Validates a schema structure and throws SchemaValidationError if invalid.
import { validateSchema } from '@rilaykit/forms';
validateSchema(schema, rilConfig, registry); // throws on errorisFormSchema(value)
Type guard that checks whether a value is a valid FormSchema shape.
import { isFormSchema } from '@rilaykit/forms';
if (isFormSchema(data)) {
const { formConfig } = fromSchema(data, rilConfig);
}Returns: value is FormSchema
Schema Types
interface FormSchema {
readonly version?: 1;
readonly id: string;
readonly defaultValues?: Record<string, unknown>;
readonly fields?: FormSchemaField[]; // flat layout
readonly rows?: FormSchemaRow[]; // row-based layout
readonly validation?: FormSchemaValidationConfig;
readonly submitOptions?: SubmitOptions;
}
interface FormSchemaField {
readonly id: string;
readonly type: string;
readonly props?: Record<string, unknown>;
readonly validation?: FieldSchemaValidation;
readonly conditions?: ConditionalBehavior;
readonly effects?: FieldSchemaEffect[];
}
interface FieldSchemaValidation {
readonly rules?: ValidationDescriptor | ValidationDescriptor[];
readonly validateOnChange?: boolean;
readonly validateOnBlur?: boolean;
readonly debounceMs?: number;
}
type ValidationDescriptor = ValidationShortcut | ValidationDescriptorObject;
type ValidationShortcut = 'required' | 'email' | 'url' | 'number';
interface ValidationDescriptorObject {
readonly type: string;
readonly message?: string;
readonly params?: Record<string, unknown>;
}SchemaRegistry
interface SchemaRegistry {
readonly validators?: Record<string, CustomValidatorFactory>;
readonly effects?: Record<string, SchemaEffectHandler>;
}
type CustomValidatorFactory = (
params?: Record<string, unknown>,
message?: string
) => StandardSchema;
type SchemaEffectHandler = (
newValue: unknown,
context: FieldEffectContext,
params?: Record<string, unknown>
) => void | Promise<void>;SchemaValidationError
class SchemaValidationError extends Error {
readonly code = 'SCHEMA_VALIDATION_ERROR';
readonly issues: SchemaIssue[];
}
interface SchemaIssue {
readonly path: string;
readonly message: string;
readonly severity: 'error' | 'warning';
}@rilaykit/workflow
The workflow package provides components, a builder, and hooks for building multi-step workflows.
Components
Workflow
Top-level workflow wrapper. Accepts either a built WorkflowConfig or a flow builder (auto-builds internally).
import { Workflow, WorkflowBody, WorkflowStepper, WorkflowNextButton } from '@rilaykit/workflow';
<Workflow
workflowConfig={myWorkflow}
defaultValues={{ name: '' }}
defaultStep="step-1"
onStepChange={(from, to, context) => {}}
onWorkflowComplete={(data) => {}}
className="my-workflow"
>
<WorkflowStepper />
<WorkflowBody />
<WorkflowNextButton />
</Workflow>| Prop | Type | Description |
|---|---|---|
workflowConfig | WorkflowConfig | flow | Workflow configuration or builder instance |
children | React.ReactNode | Workflow content |
defaultValues? | Record<string, unknown> | Initial data |
defaultStep? | string | Step ID to start on |
onStepChange? | (from: number, to: number, context: WorkflowContext) => void | Step change callback |
onWorkflowComplete? | (data: Record<string, unknown>) => void | Promise<void> | Completion callback |
className? | string | CSS class |
WorkflowBody
Renders the current step's form content.
| Prop | Type | Description |
|---|---|---|
stepId? | string | Only render if this is the current step |
children? | React.ReactNode | Custom content (falls back to FormBody) |
WorkflowStepper
Renders the step progress indicator.
| Prop | Type | Description |
|---|---|---|
onStepClick? | (stepIndex: number) => void | Custom step click handler |
className? | string | CSS class |
WorkflowNextButton
Next/submit button with automatic state management.
| Prop | Type | Description |
|---|---|---|
isSubmitting? | boolean | Override submitting state |
className? | string | CSS class |
WorkflowPreviousButton
Previous button with automatic state management.
| Prop | Type | Description |
|---|---|---|
isSubmitting? | boolean | Override submitting state |
className? | string | CSS class |
WorkflowSkipButton
Skip button, only active when the current step allows skipping.
| Prop | Type | Description |
|---|---|---|
isSubmitting? | boolean | Override submitting state |
className? | string | CSS class |
flow (FlowBuilder)
Builder class for creating multi-step workflow configurations.
Creating a flow builder
import { flow } from '@rilaykit/workflow';
const myWorkflow = flow.create(rilConfig, 'onboarding', 'User Onboarding', 'Optional description');const myWorkflow = new flow(rilConfig, 'onboarding', 'User Onboarding');Parameters:
config: ril<any>-- The ril configuration instanceworkflowId: string-- Unique workflow identifierworkflowName: string-- Display namedescription?: string-- Optional description
.step(stepDef) / .step([stepDefs])
Adds one or multiple steps to the workflow.
workflow
.step({
id: 'personal-info',
title: 'Personal Information',
formConfig: personalForm,
allowSkip: false,
metadata: { icon: 'user' },
onAfterValidation: async (stepData, helper, context) => {
const company = await fetchCompany(stepData.siren);
helper.setNextStepFields({ companyName: company.name });
},
})
.step([
{ title: 'Step 2', formConfig: form2 },
{ title: 'Step 3', formConfig: form3, allowSkip: true },
]);StepDefinition:
interface StepDefinition {
id?: string; // Auto-generated if omitted
title: string;
description?: string;
formConfig: FormConfiguration | form; // Builder is auto-built
allowSkip?: boolean;
conditions?: StepConditionalBehavior;
metadata?: Record<string, any>;
onAfterValidation?: (
stepData: Record<string, any>,
helper: StepDataHelper,
context: WorkflowContext,
) => void | Promise<void>;
}The step lifecycle callback is onAfterValidation, not onEnter or onExit. It fires after successful validation and before navigating to the next step.
Returns: this (chainable)
.configure(options)
Configures workflow-level options.
workflow.configure({
analytics: {
onWorkflowStart: (id, context) => {},
onStepComplete: (id, duration, data, context) => {},
},
persistence: {
adapter: localStorageAdapter,
options: { autoPersist: true },
userId: 'user-123',
},
});Parameters:
options: { analytics?: WorkflowAnalytics, persistence?: { adapter, options?, userId? } }
Returns: this (chainable)
.use(plugin)
Installs a workflow plugin. Validates plugin dependencies before installation.
workflow.use(myPlugin);Returns: this (chainable)
.removePlugin(name)
Removes a plugin by name.
Returns: this (chainable)
.updateStep(id, updates)
Updates an existing step configuration.
Returns: this (chainable)
.stepConditions(id, conditions)
Adds conditional behavior to a step after creation.
workflow.stepConditions('payment', {
visible: when('hasPayment').equals(true).build(),
skippable: when('balance').equals(0).build(),
});Returns: this (chainable)
.removeStep(id)
Removes a step from the workflow.
Returns: this (chainable)
.getStep(id)
Retrieves a step configuration by ID.
Returns: StepConfig | undefined
.getSteps()
Returns a copy of all step configurations.
Returns: StepConfig[]
.clearSteps()
Removes all steps and resets the ID generator.
Returns: this (chainable)
.clone(newId?, newName?)
Creates a deep copy of the workflow builder.
Returns: flow (new instance)
.validate()
Checks for structural issues (empty workflow, duplicate IDs, missing plugin dependencies).
Returns: string[]
.build()
Builds the final WorkflowConfig. Throws if validation fails.
Returns: WorkflowConfig
.toJSON() / .fromJSON(json)
Serialization and deserialization of the workflow structure.
.getStats()
Returns workflow statistics.
const stats = workflow.getStats();
// {
// totalSteps, totalFields, averageFieldsPerStep,
// maxFieldsInStep, minFieldsInStep, hasAnalytics
// }Hooks
Context Hook
| Hook | Signature | Returns |
|---|---|---|
useWorkflowContext | useWorkflowContext() | WorkflowContextValue |
useWorkflowContext() provides the full workflow context including state, navigation, data, and submission methods. See the WorkflowContextValue type below for details.
Store Hooks (Zustand)
| Hook | Signature | Returns |
|---|---|---|
useCurrentStepIndex | useCurrentStepIndex() | number |
useWorkflowTransitioning | useWorkflowTransitioning() | boolean |
useWorkflowInitializing | useWorkflowInitializing() | boolean |
useWorkflowSubmitting | useWorkflowSubmitting() | boolean |
useWorkflowAllData | useWorkflowAllData() | Record<string, unknown> |
useWorkflowStepData | useWorkflowStepData() | Record<string, unknown> |
useStepDataById | useStepDataById(stepId: string) | Record<string, unknown> | undefined |
useVisitedSteps | useVisitedSteps() | Set<string> |
usePassedSteps | usePassedSteps() | Set<string> |
useIsStepVisited | useIsStepVisited(stepId: string) | boolean |
useIsStepPassed | useIsStepPassed(stepId: string) | boolean |
useWorkflowNavigationState | useWorkflowNavigationState() | { currentStepIndex, isTransitioning, isSubmitting } |
useWorkflowSubmitState | useWorkflowSubmitState() | { isSubmitting, isTransitioning, isInitializing } |
useWorkflowActions | useWorkflowActions() | UseWorkflowActionsResult |
interface UseWorkflowActionsResult {
setCurrentStep: (stepIndex: number) => void;
setStepData: (data: Record<string, unknown>, stepId: string) => void;
setAllData: (data: Record<string, unknown>) => void;
setFieldValue: (fieldId: string, value: unknown, stepId: string) => void;
setSubmitting: (isSubmitting: boolean) => void;
setTransitioning: (isTransitioning: boolean) => void;
setInitializing: (isInitializing: boolean) => void;
markStepVisited: (stepId: string) => void;
markStepPassed: (stepId: string) => void;
reset: () => void;
loadPersistedState: (state: Partial<WorkflowStoreState>) => void;
}Metadata Hook
| Hook | Signature | Returns |
|---|---|---|
useStepMetadata | useStepMetadata() | UseStepMetadataReturn |
interface UseStepMetadataReturn {
current: Record<string, any> | undefined;
getByStepId: (stepId: string) => Record<string, any> | undefined;
getByStepIndex: (stepIndex: number) => Record<string, any> | undefined;
hasCurrentKey: (key: string) => boolean;
getCurrentValue: <T>(key: string, defaultValue?: T) => T;
getAllStepsMetadata: () => Array<{ id, title, index, metadata }>;
findStepsByMetadata: (predicate) => string[];
}Types
Validation
interface ValidationResult {
readonly isValid: boolean;
readonly errors: ValidationError[];
readonly value?: any;
}
interface ValidationError {
readonly message: string;
readonly code?: string;
readonly path?: string;
}
type ValidationState = 'idle' | 'validating' | 'valid' | 'invalid';
interface FieldValidationConfig<T = any> {
readonly validate?: StandardSchemaV1<T> | StandardSchemaV1<T>[];
readonly validateOnChange?: boolean;
readonly validateOnBlur?: boolean;
readonly debounceMs?: number;
}
interface FormValidationConfig<T extends Record<string, any> = Record<string, any>> {
readonly validate?: StandardSchemaV1<T> | StandardSchemaV1<T>[];
readonly validateOnSubmit?: boolean;
readonly validateOnStepChange?: boolean;
}Conditions
interface ConditionConfig {
field: string;
operator: ConditionOperator;
value?: ConditionValue;
conditions?: ConditionConfig[];
logicalOperator?: 'and' | 'or';
}
type ConditionOperator =
| 'equals' | 'notEquals'
| 'greaterThan' | 'lessThan' | 'greaterThanOrEqual' | 'lessThanOrEqual'
| 'contains' | 'notContains'
| 'in' | 'notIn'
| 'matches'
| 'exists' | 'notExists';
type ConditionValue = string | number | boolean | null | undefined | Array<string | number | boolean>;
interface ConditionalBehavior {
readonly visible?: ConditionConfig;
readonly disabled?: ConditionConfig;
readonly required?: ConditionConfig;
readonly readonly?: ConditionConfig;
}
interface StepConditionalBehavior {
readonly visible?: ConditionConfig;
readonly skippable?: ConditionConfig;
}Field and Form State
interface FieldConditions {
readonly visible: boolean;
readonly disabled: boolean;
readonly required: boolean;
readonly readonly: boolean;
}
interface FieldState {
readonly value: unknown;
readonly errors: ValidationError[];
readonly validationState: ValidationState;
readonly touched: boolean;
readonly dirty: boolean;
}Workflow
interface WorkflowAnalytics {
readonly onWorkflowStart?: (workflowId: string, context: WorkflowContext) => void;
readonly onWorkflowComplete?: (workflowId: string, duration: number, data: any) => void;
readonly onWorkflowAbandon?: (workflowId: string, currentStep: string, data: any) => void;
readonly onStepStart?: (stepId: string, timestamp: number, context: WorkflowContext) => void;
readonly onStepComplete?: (stepId: string, duration: number, data: any, context: WorkflowContext) => void;
readonly onStepSkip?: (stepId: string, reason: string, context: WorkflowContext) => void;
readonly onError?: (error: Error, context: WorkflowContext) => void;
}
interface WorkflowPlugin {
readonly name: string;
readonly version?: string;
readonly install: (workflow: any) => void;
readonly dependencies?: string[];
}
interface MonitoringConfig {
readonly enabled: boolean;
readonly enablePerformanceTracking?: boolean;
readonly enableErrorTracking?: boolean;
readonly enableMemoryTracking?: boolean;
readonly performanceThresholds?: PerformanceThresholds;
readonly sampleRate?: number;
readonly bufferSize?: number;
readonly flushInterval?: number;
readonly onEvent?: (event: MonitoringEvent) => void;
readonly onBatch?: (events: MonitoringEvent[]) => void;
readonly onError?: (error: Error) => void;
}