Rilaykit Logorilaykit ✨
Workflow

Building Workflows

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

A workflow is a sequence of steps, where each step is usually a Rilaykit form. The workflow builder allows you to chain these steps together and configure the navigation logic between them.

The flow Builder

When you import from @rilaykit/workflow, the flow method is added to your ril instance's prototype via module augmentation. This allows you to create a flow builder directly from your configured ril instance.

1. Create your Form Configurations

First, you need a formConfig for each step of your workflow. You'll create these using the standard form builder.

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

const personalInfoForm = rilay.form('personal-info').add(...).build();
const preferencesForm = rilay.form('preferences').add(...).build();
const reviewForm = rilay.form('review').add(...).build();

You can also pass a form builder instance directly to a step. It will be built automatically.

2. Start the Flow Builder

Call .flow() on your ril instance to begin. You must provide a unique ID and a title for the workflow.

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

const workflowBuilder = rilay
  .flow(
    'user-onboarding',
    'User Onboarding Workflow',
    'A multi-step workflow to onboard new users' // Optional description
  )

3. Add Steps

Use the .addStep() method for each step in your sequence. You must provide at least a formConfig (or a form instance).

//...
.addStep({
  id: 'personal-info',
  title: 'Personal Information',
  description: 'Tell us about yourself',
  formConfig: personalInfoForm,
})
.addStep({
  id: 'preferences',
  title: 'Preferences',
  description: 'Set your preferences',
  formConfig: preferencesForm,
  allowSkip: true, // This step is optional
  requiredToComplete: false,
})
.addStep({
  id: 'review',
  title: 'Review & Feedback',
  description: 'Review your information',
  formConfig: reviewForm,
})
// ...

4. Configure Navigation and Hooks

You can configure the overall workflow behavior with the unified .configure() method.

// ...
.configure({
  navigation: {
    allowBackNavigation: true, // Can users go back to previous steps?
    showProgress: true, // Should the stepper be shown?
  },
  analytics: {
    onWorkflowStart: (workflowId) => console.log(`Workflow ${workflowId} started.`),
    onStepComplete: (stepId, duration) => console.log(`Step ${stepId} took ${duration}ms.`),
    // ... other analytics hooks
  }
})
// ...

5. Build the Configuration

Finally, call .build() to get the final workflowConfig object.

const workflowConfig = rilay
  .flow(...)
  .addStep(...)
  .addStep(...)
  .configure(...)
  .build();

This configuration can now be passed to the <Workflow> component, which will build it automatically if you pass the builder instance.

When to use .build()

Just like with forms, you often don't need to call .build() on a workflow builder. The <Workflow /> component handles it for you.

You should call .build() manually when you need the final, serializable WorkflowConfig object, for example, to save it as JSON or for debugging.

Step Configuration

The object passed to .addStep() has several options:

  • id: A unique identifier for the step.
  • title: A human-readable title for the step, often shown in the stepper.
  • description (optional): A short description of the step.
  • formConfig: The formConfig object for this step's form.
  • renderer (optional): A custom renderer for the body of this specific step, allowing you to override the default <FormBody> behavior.
  • allowSkip (optional, default: false): If true, the user can skip this step. The <WorkflowSkipButton> will appear.
  • requiredToComplete (optional, default: true): If false, the user can complete the workflow without having visited this step.
  • onAfterValidation (optional): A powerful hook that runs after the step's form is successfully validated, but before navigating to the next step. See Advanced Workflows for details.
  • permissions (optional): Logic to determine if a user can access the step.
  • dynamicConfig (optional): Configuration for dynamically generating steps.

Complete Example: User Onboarding Workflow

Let's put it all together. Here is a full example of a 3-step user onboarding workflow.

First, we define the forms for each step:

// 1. Define the forms for each step
const accountForm = rilay
  .form('account-form')
  .add(
    { id: 'email', type: 'email', props: { label: 'Email' }, validation: { validators: [required(), email()] } },
    { id: 'password', type: 'password', props: { label: 'Password' }, validation: { validators: [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', // Assumes a 'checkbox' component is registered
    props: {
      label: 'I agree to the terms and conditions',
    },
    validation: {
      validators: [
        custom(value => value === true, 'You must accept the terms')
      ]
    }
  });

About Validation

This example uses several built-in validators. To learn more about how validation works in Rilay, check out our in-depth Field and Form Validation guide.

Now, we use the flow builder to chain them together into a workflow:

// 2. Build the workflow
export const onboardingWorkflow = rilay
  .flow(
    'onboarding',
    'New User Onboarding'
  )
  .addStep({
    id: 'account-creation',
    title: 'Create Account',
    formConfig: accountForm, // Notice we pass the builder directly
  })
  .addStep({
    id: 'personal-info',
    title: 'Your Profile',
    formConfig: profileForm, // Rilay builds it for you
  })
  .addStep({
    id: 'confirmation',
    title: 'Confirmation',
    formConfig: confirmationForm,
  })
  .configure({
    navigation: {
      allowBackNavigation: true,
      showProgress: true,
    }
  });

This onboardingWorkflow builder instance is ready to be passed to the <Workflow /> component, providing a complete multi-step form experience out of the box.