Formily Core Fundamentals
SKILL.md
Formily Core Fundamentals
This skill provides essential guidance for using Formily as a React form solution. Focus on understanding core concepts, setting up Formily projects, and implementing basic forms with TypeScript.
Core Concepts Overview
Formily is a performant, flexible, and extensible form solution for React. It provides:
- Schema-driven forms - Define forms through JSON schema
- Path-based value management - Access nested form values via path strings
- Powerful validation - Built-in validation with custom rules
- Fine-grained reactivity - Efficient updates and minimal re-renders
- TypeScript support - Full type safety for forms and validation
Installation and Setup
Install core Formily packages:
npm install @formily/core @formily/react @formily/validate
For TypeScript support, install types:
npm install -D @types/react @types/react-dom
Basic Form Implementation
Create a simple contact form to understand Formily fundamentals:
1. Define Form Schema
import { createForm } from '@formily/core'
const form = createForm({
initialValues: {
name: '',
email: '',
message: ''
}
})
2. Connect Form to React
import { FormProvider, createField } from '@formily/react'
// In your component
const MyForm = () => {
return (
<FormProvider form={form}>
{/* Form fields go here */}
</FormProvider>
)
}
3. Create Form Fields
import { Field } from '@formily/react'
const NameField = () => (
<Field name="name">
{(field, state) => (
<div>
<label>Name:</label>
<input
value={state.value || ''}
onChange={e => field.onInput(e.target.value)}
/>
{state.errors && <span>{state.errors}</span>}
</div>
)}
</Field>
)
Form State Management
Formily manages form state through the form instance:
Accessing Form Values
// Get all form values
const values = form.values
// Get specific field value
const name = form.values.name
// Get nested values
const address = form.values.contact.address
Setting Form Values
// Set single field
form.setValues({
name: 'John Doe'
})
// Set multiple fields
form.setValues({
name: 'John Doe',
email: 'john@example.com'
})
// Set nested values
form.setValues({
'user.profile.name': 'John Doe'
})
Field State Management
// Access field state
const field = form.query('name')
const fieldValue = field.value
const fieldErrors = field.errors
const fieldTouched = field.touched
// Modify field state
form.setFieldState('name', state => {
state.value = 'New value'
state.errors = ['Error message']
})
Validation Basics
Formily provides built-in validation capabilities:
Schema Validation
import { createForm } from '@formily/core'
const form = createForm({
schema: {
type: 'object',
properties: {
name: {
type: 'string',
title: 'Name',
required: true,
'x-validator': [
{ required: true, message: 'Name is required' },
{ min: 2, message: 'Name must be at least 2 characters' }
]
},
email: {
type: 'string',
title: 'Email',
format: 'email',
'x-validator': [
{ format: 'email', message: 'Invalid email format' }
]
}
}
}
})
Custom Validation Rules
import { createForm } from '@formily/core'
const form = createForm({
validateFirst: true,
effects() {
onFieldChange('confirmPassword', ['value'], (field) => {
const password = form.values.password
const confirmPassword = field.value
if (password !== confirmPassword) {
field.errors = ['Passwords do not match']
} else {
field.errors = []
}
})
}
})
Form Submission
Handle form submission with proper validation:
const handleSubmit = async () => {
try {
// Validate entire form
await form.validate()
// Get form values
const values = form.values
console.log('Form submitted:', values)
// Reset form if needed
form.reset()
} catch (errors) {
console.error('Validation errors:', errors)
}
}
TypeScript Integration
Formily provides full TypeScript support:
Define Form Types
interface ContactForm {
name: string
email: string
message: string
}
// Create typed form
const form = createForm<ContactForm>({
initialValues: {
name: '',
email: '',
message: ''
}
})
// Access typed values
const name: string = form.values.name
Type-Safe Field Components
import { Field } from '@formily/react'
interface StringFieldProps {
name: string
label: string
placeholder?: string
}
const StringField: React.FC<StringFieldProps> = ({ name, label, placeholder }) => (
<Field name={name}>
{(field, state) => (
<div>
<label>{label}:</label>
<input
value={state.value || ''}
onChange={e => field.onInput(e.target.value)}
placeholder={placeholder}
/>
{state.errors && <span className="error">{state.errors[0]}</span>}
</div>
)}
</Field>
)
Common Patterns
Conditional Fields
const form = createForm({
effects() {
onFieldChange('hasAccount', ['value'], (field) => {
const hasAccount = field.value
form.setFieldState('password', state => {
state.visible = hasAccount
if (!hasAccount) {
state.value = ''
}
})
})
}
})
Dynamic Field Arrays
// Add item to array
form.pushValues('emails', '')
// Remove item from array
form.removeValues('emails.0')
// Insert item at specific position
form.insertValues('emails.1', 'new@email.com')
Performance Optimization
Minimize Re-renders
import { observer } from '@formily/reactive-react'
const OptimizedField = observer(({ name }: { name: string }) => (
<Field name={name}>
{(field, state) => (
<input
value={state.value || ''}
onChange={e => field.onInput(e.target.value)}
/>
)}
</Field>
))
Batch Updates
// Batch multiple updates for better performance
form.batch(() => {
form.setValues({ field1: 'value1' })
form.setValues({ field2: 'value2' })
form.setValues({ field3: 'value3' })
})
Error Handling
Global Error Handler
const form = createForm({
effects() {
onError((error) => {
console.error('Form error:', error)
// Handle global form errors
})
}
})
Field-Specific Error Handling
const form = createForm({
effects() {
onFieldError('email', (field, errors) => {
if (errors.includes('Invalid format')) {
// Show custom error UI
}
})
}
})
Additional Resources
Reference Files
references/types-cheatsheet.md- Common TypeScript types and interfacesreferences/validation-rules.md- Built-in validation rules reference
Example Files
examples/simple-contact-form.tsx- Complete working contact form exampleexamples/field-components.tsx- Reusable field component patterns
Common Formily Patterns
- Use
form.submit()for form submission with validation - Leverage
form.reset()for clearing form state - Use
form.setFieldState()for granular field control - Implement custom validators with
x-validatoror effects
Best Practices
- Always define TypeScript interfaces for your form schemas
- Use schema validation instead of manual validation when possible
- Leverage observer for performance-critical components
- Handle errors gracefully at both form and field levels
- Use batch operations for multiple state updates
- Create reusable field components to reduce code duplication
- Test validation thoroughly with edge cases and error scenarios