FieldCraftDocsServicesBlogWork With Me →

Sections & Multi-Step

Build wizard-style multi-step forms with sections.

A schema with multiple sections automatically renders as a wizard-style multi-step form. The engine manages navigation, progress tracking, and per-section validation.

Defining Sections

const schema: FormEngineSchema = {
id: "onboarding",
version: "1.0.0",
title: "Onboarding",
submitAction: { type: "callback" },
sections: [
{
id: "personal",
title: "Personal Information",
description: "Tell us about yourself",
questions: [
{ id: "name", type: "short_text", label: "Full Name", required: true },
{ id: "dob", type: "date", label: "Date of Birth" },
],
},
{
id: "contact",
title: "Contact Details",
questions: [
{ id: "email", type: "email", label: "Email", required: true },
{ id: "phone", type: "phone", label: "Phone" },
],
},
{
id: "preferences",
title: "Preferences",
questions: [
{ id: "newsletter", type: "boolean", label: "Subscribe to newsletter?" },
],
},
],
};

Section Properties

PropertyTypeRequiredDescription
idstringYesUnique section identifier
titlestringYesDisplayed in progress UI
descriptionstringNoShown below the title
showIfConditionExpressionNoConditional section visibility
onExitSectionExitActionNoBranching / jump rules
questionsQuestion[]YesFields in this section

Navigation

The engine exposes three navigation methods:

const engine = createEngine(schema);

// Move forward (validates current section first)
engine.nextSection();

// Move backward
engine.prevSection();

// Jump to a specific section by ID
engine.jumpTo("preferences");

Navigation State

The form state includes everything the UI needs to render navigation controls and progress indicators:

const state = engine.getState();

state.currentSectionId; // "contact"
state.currentSectionIndex; // 1 (0-based)
state.visibleSectionIds; // ["personal", "contact", "preferences"]
state.visitedSectionIds; // ["personal", "contact"]
state.totalVisibleSections; // 3
state.progressPercent; // 66 (visited / total * 100)
state.isCurrentSectionValid; // true/false

Section Validation

Before navigating forward, you can validate the current section. The engine checks all visible, non-structural fields:

// Validate a specific section
const result = engine.validateSection("personal");

result.valid; // boolean
result.errors; // Record<string, string[]>
result.firstErrorFieldId; // "name" (first field with errors)
result.firstErrorSectionId; // "personal"

// Validate the entire form
const fullResult = engine.validate();

Hidden fields (those with a showIf that evaluates to false) and structural fields (info_block, consent) are skipped during section validation.

Branching with Exit Rules

Use onExit to define conditional jumps when leaving a section. Rules are evaluated in order — the first matching rule wins.

{
id: "triage",
title: "Triage",
questions: [
{
id: "issue_type",
type: "single_select",
label: "What do you need help with?",
options: [
{ label: "Billing", value: "billing" },
{ label: "Technical", value: "technical" },
{ label: "Other", value: "other" },
],
},
],
onExit: {
rules: [
{
condition: { field: "issue_type", operator: "eq", value: "billing" },
jumpTo: "billing_details",
},
{
condition: { field: "issue_type", operator: "eq", value: "technical" },
jumpTo: "technical_details",
},
],
default: "general_details", // Fallback if no rule matches
},
}

Jump Rule Resolution

  1. Evaluate each rule's condition in order.
  2. If a condition matches, jump to its jumpTo section.
  3. If no rules match and default is set, jump to the default section.
  4. If no rules match and no default, proceed to the next sequential visible section.

Conditional Sections

Sections with showIf are automatically skipped when their condition is false. Navigation, progress, and validation all respect section visibility.

{
id: "employer_info",
title: "Employer Information",
showIf: { field: "employment_status", operator: "eq", value: "employed" },
questions: [
{ id: "company", type: "short_text", label: "Company Name", required: true },
],
}

Progress Tracking

Configure progress display in the schema settings:

settings: {
showProgress: true,
progressStyle: "steps", // "bar" | "steps" | "percentage"
showSectionNumbers: true,
}

Progress is calculated as visitedSections / totalVisibleSections * 100. Only visible sections count — hidden sections are excluded from both the count and the progress bar.

Navigation Settings

settings: {
navigation: {
showBack: true, // Show "Back" button
showSectionList: true, // Show section list/sidebar
nextLabel: "Continue", // Custom label for "Next"
backLabel: "Go Back", // Custom label for "Back"
allowSkip: false, // Allow skipping sections
},
}