How Formily Represents the Form Structure Internally: The @formily/core Architecture
Formily represents form structure as a reactive domain model centered around a root Form instance that manages a registry of observable Field objects, coordinated by a dependency Graph and lifecycle Heart system.
Formily, Alibaba's comprehensive form solution, implements a sophisticated internal architecture to manage complex, nested form state. Understanding how Formily represents the form structure internally reveals the engine behind its efficient reactive updates and cross-framework compatibility. The entire system resides in the @formily/core package and treats forms as trees of observable objects tracked through path-based addressing.
The Root Form Model
The Form class serves as the single source of truth for all form state. Defined in [packages/core/src/models/Form.ts](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/models/Form.ts), this root node maintains global state including values, initialValues, display, and pattern properties.
Every Form instance initializes two critical subsystems during construction. The Graph (this.graph = new Graph(this)) records parent-child and inter-field dependency relationships, while the Heart (this.heart = new Heart({ lifecycles: this.lifecycles, context: this })) centralizes lifecycle callbacks like onFormInit and onFormMount.
Observable State and Registry
All public properties on the Form instance are made reactive via define(this, { … }) (lines 22‑64 in Form.ts), converting the object into a proxy tracked by @formily/reactive. The form maintains a fields registry (fields: IFormFields) that stores field instances keyed by their FormPath address (e.g., 'user.name').
Changes to form values trigger reactions through observe(this, …) (lines 68‑78), which automatically invoke triggerFormInitialValuesChange and triggerFormValuesChange to propagate updates to dependent UI components.
The Field Hierarchy
Formily models individual form elements through a class hierarchy rooted in BaseField. Each field type extends this foundation to handle specific data structures:
Field([src/models/Field.ts](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/models/Field.ts)): Concrete implementation for standard values, managing validation state, feedback, and UI properties.ObjectField([src/models/ObjectField.ts](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/models/ObjectField.ts)): Handles nested objects with auto-cleanup behavior for removed keys viamakeAutoCleanable.ArrayField([src/models/ArrayField.ts](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/models/ArrayField.ts)): Manages dynamic arrays with mutation methods likepush,remove, andmove.VoidField([src/models/VoidField.ts](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/models/VoidField.ts)): Represents layout components that lack values but participate in the form lifecycle.
FormPath Addressing
All fields are registered in form.fields using a FormPath string generated by utilities in @formily/shared. The Field constructor calls field.locate(address) to establish its position in the tree, enabling path-based operations like form.setValuesIn(['user', 'age'], 31) to traverse and modify nested state efficiently.
Dependency Tracking with the Graph System
The [Graph.ts](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/models/Graph.ts) implementation maintains a directed graph of field relationships. When an ArrayField adds or removes items, or when fields declare dependencies on each other, the graph updates its adjacency lists.
This structure allows the reactive system to schedule targeted reactions only for affected nodes rather than re-rendering the entire form. The graph consults these relationships during batch updates to determine precisely which fields require recalculation or validation.
Reactive Engine Integration
Formily delegates all reactivity to the @formily/reactive library imported throughout Form.ts and Field.ts. The system leverages three core primitives:
define/observable: Convert plain objects and arrays into reactive proxies that track property access.action: Batches multiple state mutations into a single transaction to prevent intermediate states from triggering reactions.reaction/observe: Subscribe to specific data paths and execute side effects like validation or UI updates when values change.
This integration ensures that any modification to form.values automatically notifies subscribed fields and components without explicit event wiring.
Lifecycle Management with Heart
The Heart class ([src/models/Heart.ts](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/models/Heart.ts)) manages form and field lifecycles through a centralized hook system. It stores callbacks for mount, unmount, submit, and validate events, executing them in the correct order during form transitions.
Effect hooks like onFormInit and onFieldChange exposed via the Form Effect API (src/effects/*) register themselves with the Heart instance, creating a decoupled system where form logic remains separate from UI frameworks.
Practical Code Examples
Creating a Reactive Form Instance
The createForm factory function ([src/shared/externals.ts](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/shared/externals.ts)) instantiates the full reactive graph:
import { createForm } from '@formily/core';
const form = createForm({
values: { user: { name: 'Alice', age: 30 } },
});
// Fields are created lazily via path addressing
const nameField = form.createField({
name: 'user.name',
required: true,
});
// Updates trigger reactive reactions automatically
form.setValuesIn(['user', 'age'], 31);
console.log(nameField.value); // 'Alice'
Querying the Field Registry
The Query class enables pattern-based field selection:
// Select all fields with editable pattern
const editableFields = form.query('*').getAll();
// Retrieve validation feedback by path
const errors = form.queryFeedbacks({
type: 'error',
address: ['user', 'name'],
});
Observing Value Changes
Use the reactive engine directly to watch specific fields:
import { reaction } from '@formily/reactive';
reaction(
() => nameField.value,
(newVal) => {
console.log('Name changed to', newVal);
}
);
Summary
- Formily represents form structure internally as a reactive tree with a root
Formobject managing a registry ofFieldinstances keyed by FormPath addresses. - The
Graphclass tracks dependencies between fields to optimize reaction scheduling and avoid unnecessary re-renders. - All state is observable via
@formily/reactive, usingdefine()andobserve()to proxy values and trigger side effects automatically. - Field types (
Field,ObjectField,ArrayField,VoidField) extendBaseFieldto handle scalar values, nested objects, arrays, and layout components respectively. - The
Heartsystem centralizes lifecycle management, coordinating mount, unmount, and validation hooks across the form tree.
Frequently Asked Questions
How does Formily handle nested object structures internally?
Formily uses ObjectField instances to manage nested objects, automatically creating child fields for each key while maintaining parent-child relationships in the Graph. When keys are removed, the makeAutoCleanable feature automatically deletes associated field instances from the registry to prevent memory leaks.
What is the purpose of the Graph class in @formily/core?
The Graph class ([src/models/Graph.ts](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/models/Graph.ts)) maintains dependency relationships between fields, tracking both hierarchical parent-child connections and explicit cross-field dependencies. During reactive updates, the graph determines exactly which fields need recalculation or revalidation, ensuring efficient batch updates without scanning the entire form tree.
How are field values actually stored and accessed in Formily?
Field values are stored within the root Form instance's values object, not on individual field instances. Fields use FormPath utilities to compute their address (e.g., ['user', 'name']) and proxy access through setValuesIn and getValuesIn methods defined in [src/shared/internals.ts](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/shared/internals.ts). This centralized storage ensures all reactive dependencies point to a single source of truth.
Can Formily's internal structure work without React or Vue?
Yes. The @formily/core package is framework-agnostic, relying solely on the @formily/reactive engine for state management. The Form and Field models expose plain JavaScript APIs, allowing integration with any UI layer. React and Vue bindings simply subscribe to the reactive proxies created by define() and observe() in Form.ts.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →