How Formily's Validation System Works: Trigger Types and Implementation

Formily's validation system executes validator rules based on trigger types—onInput, onFocus, onBlur, or custom strings—by matching the trigger parameter in validateSelf against each rule's triggerType property.

The Formily validation system, as implemented in the alibaba/formily repository, provides a flexible architecture for executing field-level and form-level validation based on user interaction patterns. Understanding how trigger types control when validation runs is essential for building responsive forms that provide timely feedback without overwhelming users.

Core Architecture of Formily's Validation System

Formily's validation pipeline follows a three-stage process: descriptor parsing, rule normalization, and trigger-based execution. This architecture separates the definition of validation rules from their execution context.

Validator Descriptor Parsing

Validation begins with validator descriptors that are normalized by the parser utilities in packages/validator/src/parser.ts. The parseValidatorDescription and parseValidatorDescriptions functions (lines 21-44) transform string, function, or object descriptors into standardized IValidatorRules objects. Each rule object may optionally include a triggerType field that specifies when the validation should execute.

Rule-to-Function Conversion

Once parsed, parseValidatorRules (lines 46-70) walks through the rule keys and retrieves built-in validator functions from the internal registry. Each validator is wrapped with a consistent asynchronous wrapper (createValidate) that normalizes return values into IValidateResult objects containing error messages, warnings, or success states. This wrapping ensures that all validators—whether synchronous or asynchronous—return a consistent Promise-based interface.

How Trigger Types Control Validation Execution

The execution of validation rules is orchestrated by the validateSelf function in packages/core/src/shared/internals.ts. This function implements the trigger-matching logic that determines which validators run during any given validation cycle.

The Trigger Type Definition

Trigger types are defined as a TypeScript union type in packages/validator/src/types.ts (lines 49-53). The system recognizes the following standard trigger types:

  • onInput – Validates whenever the field value changes (default behavior)
  • onFocus – Validates when the field receives focus
  • onBlur – Validates when the field loses focus
  • Custom strings – Any user-defined string value (e.g., onSubmit, onChange) for application-specific validation timing

validateSelf Implementation Details

The validateSelf function implements two distinct execution modes based on the presence of a triggerType argument:

  1. Auto-collection mode (lines 62-70): When called without an explicit trigger, the function gathers all unique trigger types present in the field's validator list and executes validation for each type sequentially.

  2. Specific trigger mode (lines 82-84): When a specific triggerType is supplied (e.g., 'onBlur'), only validators whose triggerType property matches the supplied value execute.

Field Lifecycle Integration

Field components in packages/core/src/models/Field.ts automatically invoke validateSelf through lifecycle hooks that map to DOM events:

  • onInput (lines 84-85): Called on every value change
  • onFocus (lines 88-94): Called when the input receives focus
  • onBlur (lines 97-102): Called when the input loses focus

Each method calls validateSelf(this, '<event>'), passing the field instance and the trigger type string that corresponds to the interaction.

Supported Trigger Types in Formily

Formily supports four categories of validation triggers that provide granular control over when feedback appears:

onInput (Default) Validation runs continuously as the user types. This is the default behavior when no triggerType is specified in a validator rule, providing immediate feedback but potentially showing errors while the user is still entering data.

onFocus Validation executes when the user clicks or tabs into a field. This trigger is useful for displaying contextual help or pre-validation warnings before the user begins typing.

onBlur Validation runs when the user leaves the field. This pattern reduces noise during typing while ensuring data quality before the user moves to the next field.

Custom Triggers Any string value can be used as a trigger type. Developers define custom triggers like onSubmit or onStepChange and manually invoke form.validate({ triggerType: 'customName' }) to execute specific validation sets on demand.

Practical Implementation Examples

Basic Field with Default onInput Validation

The following example demonstrates the default behavior where validation runs on every keystroke:

import { createForm, FormProvider, Field } from '@formily/react';

const form = createForm();

<FormProvider form={form}>
  <Field
    name="email"
    validator={{
      format: 'email',
      required: true,
      // triggerType omitted → defaults to 'onInput'
    }}
  >
    {(field) => (
      <input
        value={field.value}
        onChange={field.onInput}
      />
    )}
  </Field>
</FormProvider>

Delayed Validation with onBlur

To validate only when the user finishes editing, specify triggerType: 'onBlur':

<Field
  name="username"
  validator={{
    validator: (value) => value?.length >= 4 ? '' : 'Username must be at least 4 characters',
    triggerType: 'onBlur',
  }}
>
  {(field) => (
    <>
      <input 
        value={field.value} 
        onChange={field.onInput}
        onBlur={field.onBlur} 
      />
      {field.errors?.map(err => <span key={err} className="error">{err}</span>)}
    </>
  )}
</Field>

Form-Level Validation with Specific Triggers

The batchValidate function in packages/core/src/shared/internals.ts supports trigger propagation from form-level calls:

// Validate only onBlur rules across all fields
await form.validate({ triggerType: 'onBlur' });

Implementing Custom Trigger Types

Define application-specific validation timing using custom strings:

const secretRule = {
  validator: (value) => value === 'secret-token' ? '' : 'Invalid access token',
  triggerType: 'onVerify',
};

// Later in your component
await form.validate({ triggerType: 'onVerify' });

Summary

  • Formily's validation system uses parseValidatorDescriptions in packages/validator/src/parser.ts to normalize validator descriptors into executable rules.
  • Trigger types are defined in packages/validator/src/types.ts as onInput, onFocus, onBlur, or custom string values.
  • validateSelf in packages/core/src/shared/internals.ts filters validators by matching the supplied trigger against each rule's triggerType property.
  • Field lifecycle methods (onInput, onFocus, onBlur) in packages/core/src/models/Field.ts automatically invoke validation with their respective trigger types.
  • Form-level validation via form.validate() supports trigger filtering, allowing batch execution of specific validation sets.

Frequently Asked Questions

What is the default trigger type in Formily?

The default trigger type is onInput. When you define a validator without explicitly specifying a triggerType property, Formily executes that validator whenever the field value changes, corresponding to the onInput event.

How do I run validation only on specific events?

Pass a triggerType option to form.validate() or define the triggerType property in your validator rules. For example, await form.validate({ triggerType: 'onBlur' }) executes only validators configured with triggerType: 'onBlur', while setting triggerType: 'onBlur' in a field validator ensures that rule only runs when the field loses focus.

Can I define custom trigger types in Formily?

Yes, Formily accepts any string value as a triggerType. The ValidatorTriggerType in packages/validator/src/types.ts is an open union type that includes onInput, onFocus, and onBlur, but you can pass custom strings like 'onSubmit' or 'onSave'. You must manually invoke form.validate({ triggerType: 'yourCustomTrigger' }) to execute validators using custom triggers.

Where does Formily store the validation logic for fields?

Validation logic is stored in the field's validator property as normalized rules. The actual execution logic resides in packages/core/src/shared/internals.ts (validateSelf and batchValidate functions), while the parsing and rule creation utilities are located in packages/validator/src/parser.ts. The field models in packages/core/src/models/Field.ts provide the lifecycle hooks that initiate validation based on user interactions.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →