formily migration guide

Formily Migration Guide

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "formily migration guide" with this command: npx skills add whinc/my-claude-plugins/whinc-my-claude-plugins-formily-migration-guide

Formily Migration Guide

This skill provides comprehensive guidance for converting existing React forms to Formily. Focus on migration strategies, common patterns, and step-by-step conversion processes for different form libraries and vanilla React forms with TypeScript.

Migration Overview

Why Migrate to Formily?

  • Better TypeScript support - Type-safe form management

  • Schema-driven approach - Declarative form definitions

  • Improved performance - Efficient reactivity and minimal re-renders

  • Powerful validation - Built-in validation with custom rules

  • Better developer experience - Less boilerplate, more maintainability

Migration Strategies

  • Incremental migration - Convert forms one at a time

  • Parallel development - Build new forms with Formily while maintaining existing ones

  • Complete rewrite - Replace entire form system at once

From Vanilla React Forms

Before: Vanilla React Form

import React, { useState, FormEvent } from 'react'

interface ContactForm { name: string email: string message: string }

const VanillaContactForm: React.FC = () => { const [formData, setFormData] = useState<ContactForm>({ name: '', email: '', message: '' })

const [errors, setErrors] = useState<Partial<ContactForm>>({}) const [isSubmitting, setIsSubmitting] = useState(false)

const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { const { name, value } = e.target setFormData(prev => ({ ...prev, [name]: value })) // Clear error when user starts typing if (errors[name as keyof ContactForm]) { setErrors(prev => ({ ...prev, [name]: '' })) } }

const validateForm = (): boolean => { const newErrors: Partial<ContactForm> = {}

if (!formData.name.trim()) {
  newErrors.name = 'Name is required'
}

if (!formData.email.trim()) {
  newErrors.email = 'Email is required'
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
  newErrors.email = 'Invalid email format'
}

if (!formData.message.trim()) {
  newErrors.message = 'Message is required'
}

setErrors(newErrors)
return Object.keys(newErrors).length === 0

}

const handleSubmit = async (e: FormEvent) => { e.preventDefault()

if (!validateForm()) {
  return
}

setIsSubmitting(true)

try {
  // Submit form data
  await submitForm(formData)
  alert('Form submitted successfully!')
  setFormData({ name: '', email: '', message: '' })
} catch (error) {
  console.error('Submission error:', error)
  alert('Submission failed. Please try again.')
} finally {
  setIsSubmitting(false)
}

}

return ( <form onSubmit={handleSubmit}> <div> <label>Name:</label> <input type="text" name="name" value={formData.name} onChange={handleChange} /> {errors.name && <span className="error">{errors.name}</span>} </div>

  &#x3C;div>
    &#x3C;label>Email:&#x3C;/label>
    &#x3C;input
      type="email"
      name="email"
      value={formData.email}
      onChange={handleChange}
    />
    {errors.email &#x26;&#x26; &#x3C;span className="error">{errors.email}&#x3C;/span>}
  &#x3C;/div>

  &#x3C;div>
    &#x3C;label>Message:&#x3C;/label>
    &#x3C;textarea
      name="message"
      value={formData.message}
      onChange={handleChange}
    />
    {errors.message &#x26;&#x26; &#x3C;span className="error">{errors.message}&#x3C;/span>}
  &#x3C;/div>

  &#x3C;button type="submit" disabled={isSubmitting}>
    {isSubmitting ? 'Submitting...' : 'Submit'}
  &#x3C;/button>
&#x3C;/form>

) }

After: Formily Version

import React from 'react' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { Form, FormItem, Input, Button } from '@formily/antd'

interface ContactForm { name: string email: string message: string }

const FormilyContactForm: React.FC = () => { const form = createForm<ContactForm>({ initialValues: { name: '', email: '', message: '' }, validateFirst: true })

const schema = { type: 'object', properties: { name: { type: 'string', title: 'Name', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': [ { required: true, message: 'Name is required' } ] }, email: { type: 'string', title: 'Email', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': [ { required: true, message: 'Email is required' }, { format: 'email', message: 'Invalid email format' } ] }, message: { type: 'string', title: 'Message', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input.TextArea', 'x-validator': [ { required: true, message: 'Message is required' } ] } } }

const SchemaField = createSchemaField({ components: { Form, FormItem, Input, Button } })

const handleSubmit = async () => { try { await form.validate() const values = form.values await submitForm(values) alert('Form submitted successfully!') form.reset() } catch (error) { console.error('Validation errors:', error) } }

return ( <FormProvider form={form}> <Form labelCol={6} wrapperCol={16}> <SchemaField schema={schema} /> <FormItem wrapperCol={{ offset: 6, span: 16 }}> <Button type="primary" onClick={handleSubmit}> Submit </Button> </FormItem> </Form> </FormProvider> ) }

From Formik

Before: Formik Form

import React from 'react' import { Formik, Form, Field, ErrorMessage } from 'formik' import * as Yup from 'yup'

const FormikExample: React.FC = () => { const initialValues = { firstName: '', lastName: '', email: '', age: '' }

const validationSchema = Yup.object({ firstName: Yup.string().required('First name is required'), lastName: Yup.string().required('Last name is required'), email: Yup.string().email('Invalid email').required('Email is required'), age: Yup.number().min(18, 'Must be at least 18').required('Age is required') })

const handleSubmit = (values: any, { setSubmitting }: any) => { submitForm(values) setSubmitting(false) }

return ( <Formik initialValues={initialValues} validationSchema={validationSchema} onSubmit={handleSubmit} > {({ isSubmitting }) => ( <Form> <div> <label>First Name</label> <Field name="firstName" type="text" /> <ErrorMessage name="firstName" component="div" /> </div>

      &#x3C;div>
        &#x3C;label>Last Name&#x3C;/label>
        &#x3C;Field name="lastName" type="text" />
        &#x3C;ErrorMessage name="lastName" component="div" />
      &#x3C;/div>

      &#x3C;div>
        &#x3C;label>Email&#x3C;/label>
        &#x3C;Field name="email" type="email" />
        &#x3C;ErrorMessage name="email" component="div" />
      &#x3C;/div>

      &#x3C;div>
        &#x3C;label>Age&#x3C;/label>
        &#x3C;Field name="age" type="number" />
        &#x3C;ErrorMessage name="age" component="div" />
      &#x3C;/div>

      &#x3C;button type="submit" disabled={isSubmitting}>
        Submit
      &#x3C;/button>
    &#x3C;/Form>
  )}
&#x3C;/Formik>

) }

After: Formily Version

import React from 'react' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { Form, FormItem, Input, InputNumber, Button } from '@formily/antd'

const FormilyFormikExample: React.FC = () => { const form = createForm({ initialValues: { firstName: '', lastName: '', email: '', age: undefined } })

const schema = { type: 'object', properties: { firstName: { type: 'string', title: 'First Name', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': [{ required: true, message: 'First name is required' }] }, lastName: { type: 'string', title: 'Last Name', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': [{ required: true, message: 'Last name is required' }] }, email: { type: 'string', title: 'Email', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': [ { required: true, message: 'Email is required' }, { format: 'email', message: 'Invalid email format' } ] }, age: { type: 'number', title: 'Age', required: true, 'x-decorator': 'FormItem', 'x-component': 'InputNumber', 'x-validator': [ { required: true, message: 'Age is required' }, { minimum: 18, message: 'Must be at least 18' } ] } } }

const SchemaField = createSchemaField({ components: { Form, FormItem, Input, InputNumber, Button } })

const handleSubmit = async () => { try { await form.validate() const values = form.values await submitForm(values) form.reset() } catch (error) { console.error('Validation errors:', error) } }

return ( <FormProvider form={form}> <Form labelCol={6} wrapperCol={16}> <SchemaField schema={schema} /> <FormItem wrapperCol={{ offset: 6, span: 16 }}> <Button type="primary" onClick={handleSubmit}> Submit </Button> </FormItem> </Form> </FormProvider> ) }

From React Hook Form

Before: React Hook Form

import React from 'react' import { useForm, SubmitHandler } from 'react-hook-form' import { yupResolver } from '@hookform/resolvers/yup' import * as Yup from 'yup'

interface FormData { username: string email: string password: string confirmPassword: string }

const schema = Yup.object({ username: Yup.string().required('Username is required'), email: Yup.string().email('Invalid email').required('Email is required'), password: Yup.string().min(8, 'Password must be at least 8 characters').required('Password is required'), confirmPassword: Yup.string().oneOf([Yup.ref('password')], 'Passwords must match').required('Confirm password is required') })

const ReactHookFormExample: React.FC = () => { const { register, handleSubmit, formState: { errors }, watch } = useForm<FormData>({ resolver: yupResolver(schema) })

const watchedPassword = watch('password')

const onSubmit: SubmitHandler<FormData> = (data) => { console.log(data) // Submit logic }

return ( <form onSubmit={handleSubmit(onSubmit)}> <div> <label>Username</label> <input {...register('username')} /> {errors.username && <span>{errors.username.message}</span>} </div>

  &#x3C;div>
    &#x3C;label>Email&#x3C;/label>
    &#x3C;input type="email" {...register('email')} />
    {errors.email &#x26;&#x26; &#x3C;span>{errors.email.message}&#x3C;/span>}
  &#x3C;/div>

  &#x3C;div>
    &#x3C;label>Password&#x3C;/label>
    &#x3C;input type="password" {...register('password')} />
    {errors.password &#x26;&#x26; &#x3C;span>{errors.password.message}&#x3C;/span>}
  &#x3C;/div>

  &#x3C;div>
    &#x3C;label>Confirm Password&#x3C;/label>
    &#x3C;input type="password" {...register('confirmPassword')} />
    {errors.confirmPassword &#x26;&#x26; &#x3C;span>{errors.confirmPassword.message}&#x3C;/span>}
  &#x3C;/div>

  &#x3C;button type="submit">Submit&#x3C;/button>
&#x3C;/form>

) }

After: Formily Version

import React from 'react' import { createForm } from '@formily/core' import { FormProvider, createSchemaField } from '@formily/react' import { Form, FormItem, Input, Button } from '@formily/antd'

interface FormData { username: string email: string password: string confirmPassword: string }

const FormilyReactHookFormExample: React.FC = () => { const form = createForm<FormData>({ initialValues: { username: '', email: '', password: '', confirmPassword: '' }, effects() { // Custom validation for password match onFieldChange('confirmPassword', ['value'], (field) => { const password = form.values.password const confirmPassword = field.value

    if (confirmPassword &#x26;&#x26; password !== confirmPassword) {
      field.errors = ['Passwords must match']
    } else {
      field.errors = []
    }
  })
}

})

const schema = { type: 'object', properties: { username: { type: 'string', title: 'Username', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': [{ required: true, message: 'Username is required' }] }, email: { type: 'string', title: 'Email', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': [ { required: true, message: 'Email is required' }, { format: 'email', message: 'Invalid email format' } ] }, password: { type: 'string', title: 'Password', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input.Password', 'x-validator': [ { required: true, message: 'Password is required' }, { min: 8, message: 'Password must be at least 8 characters' } ] }, confirmPassword: { type: 'string', title: 'Confirm Password', required: true, 'x-decorator': 'FormItem', 'x-component': 'Input.Password', 'x-validator': [{ required: true, message: 'Please confirm your password' }] } } }

const SchemaField = createSchemaField({ components: { Form, FormItem, Input, Button } })

const handleSubmit = async () => { try { await form.validate() const values = form.values console.log(values) // Submit logic form.reset() } catch (error) { console.error('Validation errors:', error) } }

return ( <FormProvider form={form}> <Form labelCol={6} wrapperCol={16}> <SchemaField schema={schema} /> <FormItem wrapperCol={{ offset: 6, span: 16 }}> <Button type="primary" onClick={handleSubmit}> Submit </Button> </FormItem> </Form> </FormProvider> ) }

Migration Checklist

Pre-Migration Preparation

Analyze existing forms

  • Count total forms to migrate

  • Identify form complexity levels

  • Document validation logic

  • Note custom components used

Set up Formily

  • Install Formily packages

  • Configure TypeScript types

  • Set up Ant Design integration

  • Create basic form components

Plan migration strategy

  • Choose incremental vs complete rewrite

  • Define migration timeline

  • Assign team responsibilities

  • Set up testing strategy

Migration Steps

Convert form structure

  • Replace useState with createForm

  • Convert field definitions to schema

  • Update validation logic

  • Replace onChange handlers

Update validation

  • Convert Yup schemas to Formily validators

  • Implement custom validation rules

  • Set up async validation

  • Configure error messages

Migrate components

  • Replace Formik/React Hook Form components

  • Update Ant Design integration

  • Convert custom field components

  • Update form submission logic

Test migration

  • Unit test individual forms

  • Integration test form workflows

  • Test validation scenarios

  • Performance test large forms

Post-Migration

Validation

  • All forms render correctly

  • Validation works as expected

  • Form submission functions properly

  • TypeScript types are correct

Optimization

  • Remove unused dependencies

  • Optimize form performance

  • Update documentation

  • Train team on Formily

Common Migration Patterns

State Management Migration

// Before: Multiple useState hooks const [name, setName] = useState('') const [email, setEmail] = useState('') const [errors, setErrors] = useState({})

// After: Single form instance const form = createForm({ initialValues: { name: '', email: '' } })

Validation Migration

// Before: Manual validation const validateForm = () => { const errors = {} if (!name) errors.name = 'Name is required' if (!email) errors.email = 'Email is required' setErrors(errors) }

// After: Schema-based validation const schema = { type: 'object', properties: { name: { type: 'string', required: true }, email: { type: 'string', required: true, format: 'email' } } }

Form Submission Migration

// Before: Manual submission handling const handleSubmit = (e) => { e.preventDefault() if (validateForm()) { submitForm({ name, email }) } }

// After: Formily submission const handleSubmit = async () => { await form.validate() submitForm(form.values) }

Additional Resources

Templates

  • templates/migration-template.tsx

  • Reusable migration template

  • templates/validation-converter.ts

  • Validation logic converter utility

Reference Files

  • references/migration-comparison.md

  • Side-by-side comparison table

  • references/common-pitfalls.md

  • Migration pitfalls and solutions

  • references/validation-mapping.md

  • Validation rule mapping guide

Migration Tools

  • Use automated code migration scripts for simple forms

  • Create custom transformation utilities for complex forms

  • Implement gradual migration wrappers for mixed environments

Best Practices

  • Start simple - Migrate basic forms first to learn the pattern

  • Maintain type safety - Convert all forms to TypeScript

  • Test thoroughly - Validate each migrated form matches original behavior

  • Document changes - Keep track of migration decisions and patterns

  • Iterate gradually - Improve forms after initial migration

  • Monitor performance - Watch for performance issues in complex forms

  • Train the team - Ensure all developers understand Formily patterns

  • Plan for rollbacks - Keep original forms as backup during migration

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

taro documentation

No summary provided by upstream source.

Repository SourceNeeds Review
General

ahooks

No summary provided by upstream source.

Repository SourceNeeds Review
General

formily core fundamentals

No summary provided by upstream source.

Repository SourceNeeds Review
General

advanced formily patterns

No summary provided by upstream source.

Repository SourceNeeds Review