How to Create a Basic Form Using Formily's Core Package
To create a basic form using Formily's core package, instantiate a Form object by calling createForm() from @formily/core, then connect it to your UI layer using FormProvider and declare fields with SchemaField from your framework-specific binding.
The @formily/core package in the alibaba/formily repository provides the foundational state management, validation engine, and lifecycle hooks required to build robust forms programmatically. While the core package operates independently of any rendering layer, it is designed to pair with UI bindings like @formily/react or @formily/vue to handle the actual DOM representation. This guide walks through the essential steps to create a basic form using Formily's core package, referencing specific implementation details from the source code.
Instantiating the Form with createForm
The entry point for creating any Formily form is the createForm factory function exported from the core package. This function initializes a Form instance that manages field values, validation state, and submission lifecycle.
According to the source code in [packages/core/src/shared/externals.ts](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/shared/externals.ts#L36) (line 36), createForm returns a Form object configured with your specified options. The resulting instance implements methods such as setValues, validate, and submit, which are defined in the core Form class at [packages/core/src/models/Form.ts](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/models/Form.ts).
import { createForm } from '@formily/core';
const form = createForm({
validateFirst: true,
initialValues: {
username: '',
password: ''
}
});
The Form instance created here acts as the single source of truth for all field states, errors, and effects, but it does not render any UI elements on its own.
Defining Fields with SchemaField
To declare form fields declaratively, Formily uses a schema-based approach via the SchemaField component provided by UI bindings (e.g., @formily/react). The schema itself is a plain JSON object that describes field types, validation rules, and component mappings using x-component and x-decorator properties.
While the core package in packages/core/src/models/Field.ts defines the underlying Field class that tracks individual field state, the SchemaField component translates your declarative schema into these core field instances.
import { SchemaField } from '@formily/react';
<SchemaField>
<SchemaField.String
name="username"
title="Username"
required
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.String
name="password"
title="Password"
required
x-decorator="FormItem"
x-component="Password"
/>
</SchemaField>
Each SchemaField declaration automatically registers a corresponding field instance with the core Form object, linking form.values.username and form.values.password to the respective components.
Connecting to the UI Layer with FormProvider
To make the core Form instance accessible to all descendant components, you must wrap your form layout with FormProvider (or the equivalent for your framework). This component creates a context that supplies the Form object to SchemaField components and action triggers like Submit.
As implemented in the React binding documentation, FormProvider accepts the form instance as a required prop:
import { FormProvider, Submit } from '@formily/react';
export default function BasicForm() {
return (
<FormProvider form={form}>
<SchemaField>
{/* field definitions */}
</SchemaField>
<Submit onSubmit={values => console.log(values)}>
Submit
</Submit>
</FormProvider>
);
}
The Submit component triggers form.submit() internally, which validates all fields according to the rules defined in the schema and executes the onSubmit callback with the validated values upon success.
Complete Working Example
Below is a complete, runnable example demonstrating how to create a basic form using Formily's core package with the React UI binding. This setup registers Ant Design components and creates a functional login form.
// FormComponent.tsx
import React from 'react';
import { createForm } from '@formily/core';
import { FormProvider, SchemaField, Submit } from '@formily/react';
import { Input, Password, FormItem } from '@formily/antd';
// Register UI components to SchemaField types
SchemaField.define('String', {
component: Input,
decorator: FormItem,
});
SchemaField.define('Password', {
component: Password,
decorator: FormItem,
});
const form = createForm({
validateFirst: true,
});
export default function BasicForm() {
return (
<FormProvider form={form}>
<SchemaField>
<SchemaField.String
name="username"
title="Username"
required
/>
<SchemaField.Password
name="password"
title="Password"
required
/>
</SchemaField>
<Submit onSubmit={values => alert(JSON.stringify(values, null, 2))}>
Submit
</Submit>
</FormProvider>
);
}
This implementation follows the three-step workflow: (1) createForm instantiates the core state manager, (2) FormProvider injects it into the component tree, and (3) SchemaField declarations map UI components to core field instances.
Core Architecture Concepts
Understanding the underlying classes helps when customizing behavior beyond basic schema definitions.
-
Form Class: Located at [
packages/core/src/models/Form.ts](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/models/Form.ts), this class manages the global form state, including values, errors, and submission status. It provides methods likereset,validate, andsetValues. -
Field Classes: Individual fields are represented by the
Fieldclass and its subclasses (ObjectField,ArrayField) in [packages/core/src/models/Field.ts](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/models/Field.ts). These classes track field-specific state such as dirty status, validation errors, and display values. -
Lifecycle Effects: Hook into form events (initialization, mounting, submission, validation) using effect creators defined in [
packages/core/src/effects/onFormEffects.ts](https://github.com/alibaba/formily/blob/formily_next/packages/core/src/effects/onFormEffects.ts). These allow you to execute side effects when specific form actions occur.
Summary
- Initialize your form by calling
createForm()from@formily/core, which returns aForminstance defined inpackages/core/src/models/Form.ts. - Connect the form to your components using
FormProviderfrom your UI binding (e.g.,@formily/react). - Declare fields using
SchemaFieldcomponents, which map to the coreFieldclass inpackages/core/src/models/Field.ts. - Handle submission and validation through the
Forminstance methods or theSubmitcomponent, which orchestratesform.submit()andform.validate().
Frequently Asked Questions
What is the difference between @formily/core and @formily/react?
@formily/core is a framework-agnostic library that manages form state, validation logic, and field relationships through classes like Form and Field. @formily/react is a UI binding that provides React components (FormProvider, SchemaField, Submit) to render the core state. You need both to create a rendered form in a React application, but the core package can operate independently for headless form logic.
How do I access form values programmatically without using SchemaField?
You can interact directly with the Form instance created by createForm(). Access form.values to read the current state, or call form.setValuesIn('fieldName', value) to update specific fields. These methods are implemented in packages/core/src/models/Form.ts and allow imperative control when declarative schema definitions are insufficient.
Can I use @formily/core without a UI framework binding?
Yes, @formily/core is designed to be headless and can manage form state in non-UI environments such as Node.js servers or testing frameworks. You would manually instantiate fields using the Field class constructors and manage their lifecycle through the Form instance methods without ever mounting a FormProvider or SchemaField.
Where are validation rules processed in the Formily core?
Validation logic is orchestrated by the Form class methods and executed at the field level through the Field class in packages/core/src/models/Field.ts. When form.validate() is called, it triggers validation across all registered field instances, aggregating results into the form's error state before completing the submission lifecycle.
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 →