Skip to content
PlaygroundForm Builder

Reactive flow

A diagram of the form’s reactive flow — how options, internal state, and public API connect:

flowchart TD
  subgraph Options["Options (createForm)"]
    schema["schema"]
    uiSchema["uiSchema"]
    uiOptionsRegistry["uiOptionsRegistry"]
    initialValue["initialValue"]
    value["value (Bind)"]
    initialErrors["initialErrors"]
    disabled["disabled"]
    fieldsValidationMode["fieldsValidationMode"]
  end

  subgraph Internal["Internal"]
    validator["validator"]
    merger["merger"]
  end

  subgraph InternalState["Internal State"]
    pathsTrieRef["pathsTrieRef"]
    fieldsStateMap["fieldsStateMap"]
    errors["errors (FormErrors)"]
  end

  subgraph FormState["FormState"]
    formValue["value"]
    formIsSubmitted["isSubmitted"]
    formIsChanged["isChanged"]
    submission["submission"]
    fieldsValidation["fieldsValidation"]
  end

  subgraph Queries["Queries"]
    getValueSnapshot["getValueSnapshot"]
    getFieldErrors["getFieldErrors"]
    hasErrors["hasErrors"]
    getErrors["getErrors"]
    validate["validate"]
    getComponent["getComponent"]
    getFieldComponent["getFieldComponent"]
    retrieveSchema["retrieveSchema"]
    isDisabled["isDisabled"]
    hasFieldStateByPath["hasFieldStateByPath"]
  end

  subgraph Commands["Commands"]
    updateErrors["updateErrors"]
    updateFieldErrors["updateFieldErrors"]
    setValue["setValue"]
    validateField["validateField"]
    setFieldStateByPath["setFieldStateByPath"]
  end

  schema --> validator
  uiSchema --> validator
  uiOptionsRegistry --> validator
  validator --> merger
  schema --> merger
  uiSchema --> merger

  schema --> pathsTrieRef

  merger --> formValue
  schema --> formValue
  value --> formValue
  initialValue --> formValue

  pathsTrieRef --> formIsSubmitted
  pathsTrieRef --> formIsChanged
  fieldsStateMap --> formIsSubmitted
  fieldsStateMap --> formIsChanged

  validator --> submission
  validator --> fieldsValidation

  initialErrors --> errors

  formValue -.-> getValueSnapshot
  errors -.-> getFieldErrors
  errors -.-> hasErrors
  errors -.-> getErrors
  formValue -.-> validate
  FormState -.-> getComponent
  FormState -.-> getFieldComponent
  FormState -.-> retrieveSchema
  FormState -.-> isDisabled
  fieldsStateMap -.-> hasFieldStateByPath

  errors -.-> updateErrors
  errors -.-> updateFieldErrors
  formValue -.-> setValue
  fieldsValidation -.-> validateField
  fieldsStateMap -.-> setFieldStateByPath

What happens when the user interacts with the form:

flowchart TD
  domInput["DOM input event"]
  customHandler{"config.eventHandlers\n.oninput?"}
  customRun["eventHandlers.oninput()"]
  setFieldState["setFieldState(ctx, path, FIELD_INPUTTED)"]
  mapSet["fieldsStateMap.set(path, flags | FIELD_INPUTTED)"]
  isChanged["isChanged = true\n(map.size > 0)"]
  validationCheck{"fieldsValidationMode\nincludes ON_INPUT?"}
  afterCheck{"Modifier preconditions met?"}
  afterSubmitted{"AFTER_SUBMITTED set?\nisSubmitted = true?"}
  afterChanged{"AFTER_CHANGED set?\nFIELD_CHANGED set?"}
  afterTouched{"AFTER_TOUCHED set?\nFIELD_BLURRED set?"}
  skipValidation["skip validation"]
  debounce["debounce wrapper\n(default 300ms)"]
  runValidation["fieldsValidation.run(config, value)"]
  combinator{"combinator check\n(default: abortPrevious)"}
  abort["abort previous task"]
  waitPrevious["wait for previous\nto complete"]
  validate["validator.validateFieldValue\nor validateFieldValueAsync"]
  onSuccess["onSuccess"]
  updateErrors["updateFieldErrors(ctx, path, fieldErrors)"]
  hasErrors{"fieldErrors\nempty?"}
  clearFieldErrors["clear errors for path"]
  setFieldErrors["set errors for path"]
  onFailure["onFailure"]
  setErrorMsg["updateFieldErrors(ctx, path,\n[translate('validation-process-error',\n{ error })])"]
  onFieldsValidationFailure["options.onFieldsValidationFailure\n(error, config, value)"]
  done["done"]

  domInput --> customHandler
  customHandler -->|yes| customRun
  customHandler -->|no| setFieldState
  customRun --> setFieldState
  setFieldState --> mapSet
  mapSet --> isChanged
  setFieldState --> validationCheck
  validationCheck -->|yes| afterCheck
  validationCheck -->|no| done
  afterCheck --> afterSubmitted
  afterSubmitted -->|yes| afterChanged
  afterSubmitted -->|no| skipValidation
  afterChanged -->|yes| afterTouched
  afterChanged -->|no| skipValidation
  afterTouched -->|yes| debounce
  afterTouched -->|no| skipValidation
  skipValidation --> done
  debounce --> runValidation
  runValidation --> combinator
  combinator -->|abort| abort
  combinator -->|wait| waitPrevious
  combinator -->|proceed| validate
  abort --> validate
  waitPrevious --> validate
  validate --> onSuccess
  validate --> onFailure
  onSuccess --> updateErrors
  updateErrors --> hasErrors
  hasErrors -->|yes| setFieldErrors
  hasErrors -->|no| clearFieldErrors
  setFieldErrors --> done
  clearFieldErrors --> done
  onFailure --> setErrorMsg
  setErrorMsg --> onFieldsValidationFailure
  onFieldsValidationFailure --> done
flowchart TD
  domSubmit["DOM submit event"]
  prevent["e.preventDefault()"]
  run["submission.run(e)"]
  combinator{"combinator check\n(default: waitPrevious)"}
  ignore["ignore request\n(currently processing)"]
  proceed["proceed"]
  setState["setFieldState(formState, rootPath, FIELD_SUBMITTED)"]
  isSubmitted["isSubmitted = true"]
  snapshot["$state.snapshot(valueRef.current)"]
  validate["validator.validateFormValue\nor validateFormValueAsync"]
  taskState["Task state: processing"]
  taskDelayed{"delayedMs\n(default 500ms)?"}
  delayed["Task state: processing\n(delayed: true)"]
  taskTimeout{"timeoutMs\n(default 8000ms)?"}
  timeout["Task state: failed\n(reason: timeout)"]
  validateResult{"validation result"}
  valid{"result.errors\n=== undefined?"}
  updateErrors["updateErrors(formState, [])"]
  clearFields["fieldsStateMap.clear()"]
  isChanged["isChanged = false"]
  onSubmit["options.onSubmit(value, event)"]
  taskSuccess["Task state: success"]
  updateErrorsInvalid["updateErrors(formState, result.errors)"]
  onSubmitError["options.onSubmitError(result, event, formState)"]
  onFailure["onFailure"]
  updateFieldErrors["updateFieldErrors(formState, rootPath,\n[translate('validation-process-error',\n{ error })])"]
  onSubmissionFailure["options.onSubmissionFailure(error, event)"]
  taskFailed["Task state: failed"]

  domSubmit --> prevent
  prevent --> run
  run --> combinator
  combinator -->|ignore| ignore
  combinator -->|proceed| proceed
  proceed --> setState
  setState --> isSubmitted
  proceed --> snapshot
  snapshot --> validate
  validate --> taskState
  taskState --> taskDelayed
  taskDelayed -->|yes| delayed
  taskDelayed -->|no| taskTimeout
  delayed --> taskTimeout
  taskTimeout -->|yes| timeout
  taskTimeout -->|no| validateResult
  timeout --> onFailure
  validateResult --> valid
  valid -->|yes| updateErrors
  valid -->|no| updateErrorsInvalid
  updateErrors --> clearFields
  clearFields --> isChanged
  updateErrors --> onSubmit
  onSubmit --> taskSuccess
  updateErrorsInvalid --> onSubmitError
  onSubmitError --> taskFailed
  onFailure --> updateFieldErrors
  updateFieldErrors --> onSubmissionFailure
  onSubmissionFailure --> taskFailed
flowchart TD
  trigger["Reset trigger"]
  isEvent{"event provided?"}
  prevent["e.preventDefault()"]
  clearState["fieldsStateMap.clear()"]
  allFlags["All field state flags wiped"]
  isChanged["isChanged = false\n(map.size === 0)"]
  isSubmitted["isSubmitted = false\n(no SUBMITTED flag on root)"]
  clearErrors["errors.clear()"]
  allErrorsRemoved["All validation errors removed"]
  mergeDefaults["merger.mergeFormDataAndSchemaDefaults"]
  mergeArgs["{ formData: options.initialValue,\n  schema: options.schema }"]
  setValue["valueRef.current = merged value"]
  valueReset["Form value restored to initial state"]
  reconcile["createFormValueReconciler\n(reconciles keyed arrays)"]
  onReset["options.onReset(e)"]
  done["done"]

  trigger --> isEvent
  isEvent -->|yes| prevent
  isEvent -->|no| clearState
  prevent --> clearState
  clearState --> allFlags
  allFlags --> isChanged
  allFlags --> isSubmitted
  clearState --> clearErrors
  clearErrors --> allErrorsRemoved
  clearState --> mergeDefaults
  mergeDefaults --> mergeArgs
  mergeArgs --> setValue
  setValue --> valueReset
  valueReset --> reconcile
  reconcile --> onReset
  allErrorsRemoved --> onReset
  isChanged --> onReset
  onReset --> done