Technical Documentation

Internals: Form Engine Architecture

This document provides a deep-dive analysis of the FormEngineProvider and its core state management strategy.

1. Single Source of Truth

The engine is built around a single FormSchema object. Every aspect of the form—from UI structure to validation logic—is derived from this JSON.

The Role of FormEngineProvider

The provider initializes the react-hook-form context and bridges it with the DFBE's dynamic logic.

// Core initialization logic inside FormEngineProvider const methods = useForm({ mode: "all", defaultValues: propDefaultValues, resolver: (values) => { // Schema is re-generated on every validation cycle const dynamicSchema = generateZodSchema(allFields, values, datasetLookups); return zodResolver(dynamicSchema)(values); }, });

2. Step Navigation & Validation

Navigation in DFBE is "safe by design." You cannot move to the next step unless the current step's fields are valid.

handleNext Logic

  1. Extract Field Names: It flattens all fields in the current step into an array of paths (e.g., ["profile.name", "profile.email"]).
  2. Trigger Validation: It calls formMethods.trigger(paths).
  3. Navigation Guards: If the current step is a CustomStep or has a registered StepGuard, the engine awaits the guard's callback. If the guard returns false, navigation is blocked.
  4. State Update: Only if both field validation and guards pass, it increments currentStepIndex.
  5. UX: Automatically triggers window.scrollTo({ top: 0 }) to reset the user's view.

initialStep — Starting on a Specific Step

By default, FormEngineProvider starts at step index 0. To start on a specific step (e.g., resuming a partially-filled draft), pass the step's ID string via the initialStep prop.

<FormEngineProvider schema={schema} defaultValues={savedDraft} initialStep="education" // Jumps to the step with id: "education" > <MyFormShell /> </FormEngineProvider>

Important: Step IDs are assigned in the Studio Builder under Step Settings → Step ID. All step types (schema and custom) support editable IDs.

If the provided ID doesn't match any step in the schema, the engine falls back to step 0 and emits a console.warn.

Note: initialStep does NOT validate skipped steps. The host app is responsible for ensuring defaultValues contains valid data for steps before the initial step.

onStepChange — Tracking Step Position

Use onStepChange to be notified whenever the active step changes (forward or backward). This is useful for persisting the user's last-visited step for future resume.

<FormEngineProvider schema={schema} defaultValues={savedDraft} initialStep={lastStep} onStepChange={(stepId, stepIndex) => { // Persist the active step for resume localStorage.setItem(`draft_${formId}_lastStep`, stepId); }} > <MyFormShell /> </FormEngineProvider>

The callback signature is (stepId: string, stepIndex: number) => void, providing both the semantic ID (for persistence/routing) and the numeric index (for progress indicators).

setStepGuard

Host applications can register a custom validation callback via setStepGuard. This is typically used in CustomStep components to perform async checks (e.g. checking payment status) before moving forward.

3. The Dataset Lookup Optimization

When a dataset prop is provided, the engine performs a pre-computation phase during mount/update:

  • Logic: It iterates through the dataset configuration and builds a Map for every field ID associated with that dataset.
  • Complexity: This transform array -> Map is O(N) where N is the total items in the dataset.
  • Performance: Once built, every field lookup during validation or rendering is O(1).

4. Contextual Data Flow

The FormEngineContext provides 15 distinct values to consumers:

  • Navigation State: currentStep, currentStepIndex, isFirstStep, isLastStep.
  • Navigation Actions: handleNext, handleBack, setStepGuard.
  • Logic State: showSummary, setShowSummary, validatedData, onFormSubmit.
  • Form Bindings: formMethods (Raw RHF access).
  • Schema & Data: schema, dataset, datasetLookups.
  • Registries: customSteps (StepComponentRegistry via StepRegistryContext).

5. Lifecycle Guardrails

The engine includes several safety checks:

  • Empty Schema: If schema.steps is empty, it renders a hard error alert instead of crashing.
  • Unmapped Components: If a field type is not in the ComponentRegistry, it renders a placeholder warning with instructions for the developer.