Marketplace
Advanced Formily Patterns
This skill should be used when the user asks to "implement dynamic forms", "formily complex forms", "formily conditional fields", "formily nested forms", "formily async validation", "formily performance optimization", or "advanced formily patterns".
$ Instalar
git clone https://github.com/whinc/my-claude-plugins /tmp/my-claude-plugins && cp -r /tmp/my-claude-plugins/plugins/formily/skills/formily-advanced ~/.claude/skills/my-claude-plugins// tip: Run this command in your terminal to install the skill
SKILL.md
name: Advanced Formily Patterns description: This skill should be used when the user asks to "implement dynamic forms", "formily complex forms", "formily conditional fields", "formily nested forms", "formily async validation", "formily performance optimization", or "advanced formily patterns". version: 1.0.0
Advanced Formily Patterns
This skill provides advanced guidance for implementing complex form scenarios with Formily. Focus on dynamic forms, conditional fields, nested forms, async validation, performance optimization, and sophisticated form patterns in TypeScript.
Dynamic Forms
Conditional Field Visibility
import { createForm, onFieldChange } from '@formily/core'
const form = createForm({
effects() {
// Show/hide fields based on other field values
onFieldChange('userType', ['value'], (field) => {
const userType = field.value
form.setFieldState('companyName', state => {
state.visible = userType === 'business'
if (userType !== 'business') {
state.value = ''
}
})
form.setFieldState('studentId', state => {
state.visible = userType === 'student'
if (userType !== 'student') {
state.value = ''
}
})
})
}
})
Dynamic Field Options
const form = createForm({
effects() {
onFieldChange('country', ['value'], (field) => {
const country = field.value
// Update city options based on selected country
const cityOptions = country === 'us'
? [
{ label: 'New York', value: 'nyc' },
{ label: 'Los Angeles', value: 'la' },
{ label: 'Chicago', value: 'chi' }
]
: country === 'uk'
? [
{ label: 'London', value: 'lon' },
{ label: 'Manchester', value: 'man' },
{ label: 'Birmingham', value: 'bir' }
]
: []
form.setFieldState('city', state => {
state.dataSource = cityOptions
state.value = cityOptions.length > 0 ? cityOptions[0].value : ''
})
})
}
})
Dynamic Form Schema
import { createForm, Form } from '@formily/core'
interface DynamicFormConfig {
fields: Array<{
name: string
type: 'string' | 'number' | 'boolean' | 'array' | 'object'
label: string
required?: boolean
options?: Array<{ label: string; value: any }>
validation?: any[]
}>
}
const createDynamicForm = (config: DynamicFormConfig) => {
const schema = {
type: 'object',
properties: config.fields.reduce((acc, field) => {
acc[field.name] = {
type: field.type,
title: field.label,
required: field.required,
'x-validator': field.validation
}
if (field.type === 'boolean') {
acc[field.name]['x-component'] = 'Checkbox'
} else if (field.options) {
acc[field.name]['x-component'] = 'Select'
acc[field.name]['x-component-props'] = {
options: field.options
}
} else {
acc[field.name]['x-component'] = 'Input'
}
return acc
}, {} as any)
}
return createForm({ schema })
}
Array Field Patterns
Dynamic Array Items
import { createForm, isArrayField } from '@formily/core'
import { ArrayField } from '@formily/react'
const form = createForm({
initialValues: {
contacts: [
{ name: '', phone: '', relationship: '' }
]
}
})
// Add new contact item
const addContact = () => {
form.pushValues('contacts', {
name: '',
phone: '',
relationship: ''
})
}
// Remove contact item
const removeContact = (index: number) => {
form.removeValues(`contacts.${index}`)
}
// Reorder contacts
const moveContact = (fromIndex: number, toIndex: number) => {
const field = form.query('contacts')
if (isArrayField(field)) {
field.move(fromIndex, toIndex)
}
}
Nested Array Objects
interface Contact {
name: string
emails: Array<{
address: string
type: 'work' | 'personal' | 'other'
}>
}
const NestedArrayForm = () => {
const form = createForm<Contact>({
initialValues: {
name: '',
emails: []
}
})
const addEmail = () => {
form.pushValues('emails', {
address: '',
type: 'work'
})
}
return (
<FormProvider form={form}>
<ArrayField name="emails">
{(field) => (
<div>
{field.value?.map((email, index) => (
<div key={index}>
<Field name={`emails.${index}.address`}>
{/** Email input component */}
</Field>
<Field name={`emails.${index}.type`}>
{/** Email type select component */}
</Field>
</div>
))}
<button onClick={addEmail}>Add Email</button>
</div>
)}
</ArrayField>
</FormProvider>
)
}
Async Validation Patterns
Server-Side Validation
import { createForm } from '@formily/core'
const form = createForm({
effects() {
// Async email uniqueness check
onFieldChange('email', ['value'], async (field) => {
if (!field.value) return
field.validating = true
try {
const isUnique = await checkEmailUniqueness(field.value)
if (!isUnique) {
field.errors = ['Email already exists']
} else {
field.errors = []
}
} catch (error) {
field.errors = ['Validation failed. Please try again.']
} finally {
field.validating = false
}
})
// Debounced validation for performance
onFieldChange('username', ['value'],
debounce(async (field) => {
if (!field.value || field.value.length < 3) return
field.validating = true
try {
const exists = await checkUsernameExists(field.value)
field.errors = exists ? ['Username already taken'] : []
} catch (error) {
field.errors = ['Unable to validate username']
} finally {
field.validating = false
}
}, 500)
)
}
})
// Helper function for debouncing
const debounce = <T extends (...args: any[]) => any>(
func: T,
wait: number
) => {
let timeout: NodeJS.Timeout
return (...args: Parameters<T>) => {
clearTimeout(timeout)
timeout = setTimeout(() => func(...args), wait)
}
}
Complex Validation Rules
import { createForm, onFieldReact } from '@formily/core'
const form = createForm({
effects() {
// Password strength validation
onFieldReact('password', (field) => {
const password = field.value || ''
const errors = []
if (password.length < 8) {
errors.push('Password must be at least 8 characters')
}
if (!/[A-Z]/.test(password)) {
errors.push('Password must contain uppercase letter')
}
if (!/[a-z]/.test(password)) {
errors.push('Password must contain lowercase letter')
}
if (!/\d/.test(password)) {
errors.push('Password must contain number')
}
if (!/[!@#$%^&*]/.test(password)) {
errors.push('Password must contain special character')
}
field.errors = errors
})
// Confirm password validation
onFieldReact('confirmPassword', (field) => {
const password = form.values.password
const confirmPassword = field.value
if (confirmPassword && password !== confirmPassword) {
field.errors = ['Passwords do not match']
} else {
field.errors = []
}
})
// Date range validation
onFieldReact('endDate', (field) => {
const startDate = form.values.startDate
const endDate = field.value
if (startDate && endDate && new Date(endDate) <= new Date(startDate)) {
field.errors = ['End date must be after start date']
} else {
field.errors = []
}
})
}
})
Nested Form Patterns
Multi-Step Forms
import { createForm } from '@formily/core'
import { useState } from 'react'
interface MultiStepFormData {
personal: {
firstName: string
lastName: string
email: string
}
professional: {
company: string
position: string
experience: number
}
preferences: {
notifications: boolean
theme: 'light' | 'dark'
language: string
}
}
const MultiStepForm = () => {
const [currentStep, setCurrentStep] = useState(0)
const form = createForm<MultiStepFormData>({
initialValues: {
personal: { firstName: '', lastName: '', email: '' },
professional: { company: '', position: '', experience: 0 },
preferences: { notifications: true, theme: 'light', language: 'en' }
}
})
const steps = [
{ title: 'Personal Info', fields: ['personal'] },
{ title: 'Professional', fields: ['professional'] },
{ title: 'Preferences', fields: ['preferences'] }
]
const handleNext = async () => {
// Validate current step
const currentFields = steps[currentStep].fields
const isValid = await Promise.all(
currentFields.map(field => form.validate(field))
)
if (isValid.every(result => result === null)) {
if (currentStep < steps.length - 1) {
setCurrentStep(currentStep + 1)
}
}
}
const handlePrevious = () => {
if (currentStep > 0) {
setCurrentStep(currentStep - 1)
}
}
const handleSubmit = async () => {
try {
await form.validate()
const values = form.values
console.log('Form submitted:', values)
} catch (errors) {
console.error('Validation errors:', errors)
}
}
return (
<FormProvider form={form}>
{/* Step content based on currentStep */}
{/* Navigation buttons */}
</FormProvider>
)
}
Nested Object Management
interface Address {
street: string
city: string
state: string
zip: string
country: string
}
interface UserProfile {
name: string
email: string
address: Address
emergencyContact: {
name: string
relationship: string
phone: string
}
}
const NestedForm = () => {
const form = createForm<UserProfile>({
initialValues: {
name: '',
email: '',
address: {
street: '',
city: '',
state: '',
zip: '',
country: 'us'
},
emergencyContact: {
name: '',
relationship: '',
phone: ''
}
}
})
// Update nested object efficiently
const updateAddress = (field: keyof Address, value: string) => {
form.setFieldState(`address.${field}`, state => {
state.value = value
})
}
// Batch update nested object
const updateEntireAddress = (address: Partial<Address>) => {
form.setValues({
address: { ...form.values.address, ...address }
})
}
return (
<FormProvider form={form}>
{/* Nested form fields */}
</FormProvider>
)
}
Performance Optimization
Memoized Field Components
import React, { memo } from 'react'
import { observer } from '@formily/reactive-react'
import { useField } from '@formily/react'
interface OptimizedFieldProps {
name: string
component: React.ComponentType<any>
[key: string]: any
}
const OptimizedField = observer(
memo<OptimizedFieldProps>(({ name, component: Component, ...props }) => {
const field = useField(name)
return (
<div>
<Component {...props} value={field.value} onChange={field.onInput} />
{field.errors && (
<div className="error">{field.errors[0]}</div>
)}
</div>
)
})
)
// Usage
const MyForm = () => {
return (
<FormProvider form={form}>
<OptimizedField name="firstName" component={Input} />
<OptimizedField name="email" component={Input} />
</FormProvider>
)
}
Virtualized Large Forms
import { FixedSizeList as List } from 'react-window'
const VirtualizedForm = () => {
const [items] = useState(() =>
Array.from({ length: 1000 }, (_, i) => ({
id: `field-${i}`,
name: `Field ${i + 1}`,
value: ''
}))
)
const Row = ({ index, style }: { index: number; style: React.CSSProperties }) => (
<div style={style}>
<Field name={`items.${index}.value`}>
{({ field, state }) => (
<div>
<label>{items[index].name}</label>
<Input
value={state.value}
onChange={(e) => field.onInput(e.target.value)}
/>
</div>
)}
</Field>
</div>
)
return (
<FormProvider form={form}>
<List
height={400}
itemCount={items.length}
itemSize={60}
width="100%"
>
{Row}
</List>
</FormProvider>
)
}
Efficient Field Updates
// Batch multiple field updates
const batchUpdateForm = () => {
form.batch(() => {
form.setValues({ name: 'John' })
form.setValues({ email: 'john@example.com' })
form.setValues({ age: 30 })
form.setFieldState('name', state => {
state.touched = true
})
})
}
// Selective re-rendering
const SelectiveForm = () => {
const [shouldRenderExpensive, setShouldRenderExpensive] = useState(false)
return (
<FormProvider form={form}>
{/* Always render essential fields */}
<Field name="name">
{/* Simple input */}
</Field>
{/* Conditionally render expensive fields */}
{shouldRenderExpensive && (
<Field name="complexField">
{/* Expensive component */}
</Field>
)}
<button onClick={() => setShouldRenderExpensive(!shouldRenderExpensive)}>
Toggle Complex Field
</button>
</FormProvider>
)
}
Custom Field Patterns
Address Field Component
interface AddressFieldProps {
name: string
label?: string
required?: boolean
}
const AddressField: React.FC<AddressFieldProps> = ({ name, label, required }) => {
const form = useForm()
const handleCountryChange = (country: string) => {
// Reset dependent fields when country changes
form.setValues({
[`${name}.state`]: '',
[`${name}.city`]: ''
})
}
return (
<div>
<h3>{label || 'Address'}</h3>
<Row gutter={16}>
<Col span={24}>
<Field name={`${name}.street`}>
<FormItem label="Street Address" required={required}>
<Input placeholder="123 Main St" />
</FormItem>
</Field>
</Col>
</Row>
<Row gutter={16}>
<Col span={8}>
<Field name={`${name}.city`}>
<FormItem label="City" required={required}>
<Input placeholder="City" />
</FormItem>
</Field>
</Col>
<Col span={8}>
<Field name={`${name}.state`}>
<FormItem label="State/Province">
<Input placeholder="State" />
</FormItem>
</Field>
</Col>
<Col span={8}>
<Field name={`${name}.zip`}>
<FormItem label="ZIP/Postal Code">
<Input placeholder="ZIP" />
</FormItem>
</Field>
</Col>
</Row>
<Row gutter={16}>
<Col span={24}>
<Field name={`${name}.country`}>
<FormItem label="Country" required={required}>
<Select
placeholder="Select country"
onChange={handleCountryChange}
>
<Select.Option value="us">United States</Select.Option>
<Select.Option value="ca">Canada</Select.Option>
<Select.Option value="uk">United Kingdom</Select.Option>
</Select>
</FormItem>
</Field>
</Col>
</Row>
</div>
)
}
File Upload with Progress
import { Upload, Button, Progress } from 'antd'
interface FileUploadFieldProps {
name: string
maxSize?: number
accept?: string
multiple?: boolean
}
const FileUploadField: React.FC<FileUploadFieldProps> = ({
name,
maxSize = 5 * 1024 * 1024, // 5MB
accept,
multiple = false
}) => {
const [uploadProgress, setUploadProgress] = useState<Record<string, number>>({})
const handleUpload = async (file: File) => {
const formData = new FormData()
formData.append('file', file)
try {
// Simulate upload progress
let progress = 0
const interval = setInterval(() => {
progress += 10
setUploadProgress(prev => ({ ...prev, [file.name]: progress }))
if (progress >= 100) {
clearInterval(interval)
}
}, 200)
const response = await uploadFile(formData)
// Update form field with upload result
form.setValues({
[name]: response.url
})
return response
} catch (error) {
console.error('Upload failed:', error)
throw error
}
}
return (
<Field name={name}>
{({ field, state }) => (
<div>
<Upload
customRequest={({ file }) => handleUpload(file as File)}
beforeUpload={(file) => {
if (file.size > maxSize) {
alert(`File size must be less than ${maxSize / 1024 / 1024}MB`)
return false
}
return true
}}
accept={accept}
multiple={multiple}
>
<Button icon={<UploadOutlined />}>
Select {multiple ? 'Files' : 'File'}
</Button>
</Upload>
{Object.entries(uploadProgress).map(([filename, progress]) => (
<div key={filename}>
<span>{filename}</span>
<Progress percent={progress} size="small" />
</div>
))}
{state.value && (
<div>Uploaded: {state.value}</div>
)}
</div>
)}
</Field>
)
}
Additional Resources
Example Files
examples/dynamic-form.tsx- Complete dynamic form implementationexamples/performance-optimization.tsx- Performance optimization techniquesexamples/complex-validation.tsx- Advanced validation patterns
Reference Files
references/performance-checklist.md- Performance optimization checklistreferences/validation-patterns.md- Complex validation pattern referencereferences/nested-form-patterns.md- Nested form architecture guide
Common Advanced Patterns
- Dynamic field visibility and options
- Async validation with debouncing
- Nested object and array management
- Multi-step form workflows
- Performance optimization strategies
- Custom field component creation
Best Practices
- Use debouncing for async validation to prevent excessive API calls
- Batch field updates using form.batch() for better performance
- Memoize expensive components to prevent unnecessary re-renders
- Virtualize large forms with hundreds of fields
- Use TypeScript generics for type-safe nested forms
- Implement proper error boundaries for complex form scenarios
- Optimize field dependencies to minimize validation triggers
- Use form effects for reactive field behavior instead of manual listeners
Repository

whinc
Author
whinc/my-claude-plugins/plugins/formily/skills/formily-advanced
1
Stars
0
Forks
Updated4d ago
Added1w ago