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:

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 Form object managing a registry of Field instances keyed by FormPath addresses.
  • The Graph class tracks dependencies between fields to optimize reaction scheduling and avoid unnecessary re-renders.
  • All state is observable via @formily/reactive, using define() and observe() to proxy values and trigger side effects automatically.
  • Field types (Field, ObjectField, ArrayField, VoidField) extend BaseField to handle scalar values, nested objects, arrays, and layout components respectively.
  • The Heart system 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:

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 →