Skip to content

Form Flow Structure

This document explains the structure and organization of form flows, including steps, routing between steps, and data management.

A form flow is a complete multi-step form with a defined structure:

import { flow } from '@getevy/evy-ui/flow';
const myFlow = flow({
id: 'registration-flow', // Unique identifier for the flow
steps: [
// Array of step definitions
// Step definitions go here
],
queryParameters: [], // Optional: inputs that can be filled from query params
});
  • id: Unique identifier for the flow
  • steps: Array of step definitions that make up the flow
  • queryParameters: (Optional) Array of input fields that can be pre-filled from URL query parameters

A step represents a single screen or page in the form flow:

import { step, textInput } from '@getevy/evy-ui/flow';
const personalInfoStep = step({
id: 'personal-info', // Unique identifier for the step
blocks: [
// Array of content blocks and input fields
textInput({ id: 'firstName' }),
textInput({ id: 'lastName' }),
],
next: 'address-info', // ID of the next step or special value
});
  • id: Unique identifier for the step
  • blocks: Array of content blocks and input fields to display
  • next: Where to go after this step (can be a step ID, ‘submit’, or a conditional routing object)
  • i18nNamespace: (Optional) Custom i18n namespace for translations

The simplest form of routing is to specify the ID of the next step:

step({
id: 'step-1',
blocks: [
/* ... */
],
next: 'step-2', // Go to step-2 after this one
});

To end the flow and trigger submission:

step({
id: 'final-step',
blocks: [
/* ... */
],
next: 'submit', // End the flow and trigger submission
});

Route to different steps based on user input:

import { step, dropdown, nextFromInput } from '@getevy/evy-ui/flow';
step({
id: 'user-type',
blocks: [
dropdown({
id: 'type',
values: [
{ value: 'individual', label: 'Individual' },
{ value: 'business', label: 'Business' },
],
}),
],
next: nextFromInput({
path: 'user-type.type', // Path to the input value to check
routes: [
{ value: 'individual', next: 'individual-form' },
{ value: 'business', next: 'business-form' },
],
}),
});

In this example:

  • The path points to the value we want to check (user-type.type)
  • The routes array maps possible values to their corresponding next steps
  • If the user selects “Individual”, they’ll go to the “individual-form” step
  • If the user selects “Business”, they’ll go to the “business-form” step

Form data is organized by step, with input values nested under their step IDs:

{
"step-1": {
"firstName": "John",
"lastName": "Doe"
},
"step-2": {
"email": "john.doe@example.com",
"phone": "555-123-4567"
}
}

You can create nested data structures using dot notation in input IDs:

step({
id: 'contact-info',
blocks: [
textInput({ id: 'contact.email' }),
textInput({ id: 'contact.phone' }),
textInput({ id: 'emergency.email' }),
textInput({ id: 'emergency.phone' }),
],
next: 'next-step',
});

This would result in the following data structure:

{
"contact-info": {
"contact": {
"email": "john.doe@example.com",
"phone": "555-123-4567"
},
"emergency": {
"email": "jane.doe@example.com",
"phone": "555-987-6543"
}
}
}

When a flow reaches a step with next: 'submit', the flow is complete and the onSubmit callback is triggered. The callback receives a merged object containing all data from all steps.

The Form Flow API provides helper functions for working with submitted data:

import { mergeSteps, buildFormObject } from '@getevy/evy-ui/flow';
// Merge all step data into a single object
const mergedData = mergeSteps(formData);
// Convert to FormData for API submission
const formDataObject = buildFormObject(mergedData);
  1. Use meaningful IDs: Choose descriptive IDs for flows, steps, and inputs.

  2. Group related fields: Keep related fields together in the same step.

  3. Limit step complexity: Don’t put too many fields on a single step.

  4. Use conditional routing wisely: Complex routing can make flows difficult to understand and test.

  5. Plan your data structure: Consider how your data will be used when designing your input IDs.

  6. Consider validation requirements: Group fields that have interdependent validation in the same step.