rilaykit
Workflow

Building Workflows

How to define multi-step workflows with the flow builder.

A workflow is a sequence of steps — each usually a RilayKit form — chained together with navigation logic.

The flow Builder

rilay.flow(id?, name?, description?) starts a builder. ID and name are auto-generated when omitted. Chain .step() per step, .configure() for workflow-level options, then pass the builder to <Workflow />.

import { rilay } from '@/lib/rilay';

const workflow = rilay
  .flow('user-onboarding', 'User Onboarding')
  .step({ id: 'personal-info', title: 'Personal Information', formConfig: personalInfoForm })
  .step({ id: 'preferences', title: 'Preferences', formConfig: preferencesForm, allowSkip: true });

formConfig accepts a form builder instance directly — it is built automatically.

When to use .build()

<Workflow /> builds the workflow for you. Call .build() manually only when you need the final serializable WorkflowConfig object — e.g. to save it as JSON or for debugging.

Step Configuration

Options for .step():

OptionTypeDescription
idstringUnique identifier. Auto-generated if omitted.
titlestring (required)Shown in the stepper.
descriptionstringShort step description.
formConfigFormConfiguration | form builder (required)The step's form.
rendererCustomStepRendererCustom renderer for this step's body.
allowSkipboolean | ({ allData }) => booleanEnables <FlowSkip> (from rilaykit/react). Default false.
metadataStepMetadataicon, category, tags, and custom fields — readable via hooks and context.
conditionsStepConditionalBehaviorConditional visibility and skippable state.
after(step: StepContext) => void | Promise<void>Runs after successful validation, before navigation. See Advanced Workflows.
onAfterValidationdeprecatedLegacy 3-parameter callback. Use after.

.configure() accepts analytics (WorkflowAnalytics callbacks) and persistence ({ adapter, options?, userId? }).

Complete Example: User Onboarding Workflow

import { custom, email, minLength, required } from 'rilaykit';
import { rilay } from '@/lib/rilay';

// 1. Define the forms
const accountForm = rilay.form('account-form').add(
  { id: 'email', type: 'email', props: { label: 'Email' }, validation: { validate: [required(), email()] } },
  { id: 'password', type: 'password', props: { label: 'Password' }, validation: { validate: [required(), minLength(8)] } }
);

const profileForm = rilay.form('profile-form').add(
  { id: 'firstName', type: 'text', props: { label: 'First Name' } },
  { id: 'lastName', type: 'text', props: { label: 'Last Name' } }
);

const confirmationForm = rilay.form('confirmation-form').add({
  id: 'terms',
  type: 'checkbox',
  props: { label: 'I agree to the terms and conditions' },
  validation: { validate: [custom(value => value === true, 'You must accept the terms')] },
});

// 2. Chain them into a workflow
export const onboardingWorkflow = rilay
  .flow('onboarding', 'New User Onboarding')
  .step({
    id: 'account-creation',
    title: 'Create Account',
    formConfig: accountForm,
    metadata: { icon: 'user', category: 'account' },
    after: async (step) => {
      // Pre-fill the next visible step with the user's email
      step.next.prefill({ email: step.data.email });
    },
  })
  .step({ id: 'personal-info', title: 'Your Profile', formConfig: profileForm })
  .step({ id: 'confirmation', title: 'Confirmation', formConfig: confirmationForm })
  .configure({
    analytics: {
      onWorkflowStart: (id) => console.log(`Started: ${id}`),
      onWorkflowComplete: (id, duration, data) => console.log(`Completed: ${id} in ${duration}ms`, data),
    },
  });

Pass onboardingWorkflow to <Workflow />. See Field and Form Validation for the validators used above.

On this page