Form
High-performance form controls with built-in data field management. Including data entry, verification and corresponding styles.
Install
npm install antd-mobile@5.43.0
antd-mobile is for mobile web, not React Native or Mini Programs. React 19 projects must register createRoot with unstableSetRender at the entry, or imperative APIs such as Toast and Dialog cannot render. Switch dark mode with <html data-prefers-color-scheme="dark">.
Examples come from the official Ant Design Mobile repository (MIT) at the commit published as npm 5.43.0. Presentation helpers such as DemoBlock are JGuang rewrites, not antd-mobile APIs.
High-performance form controls with built-in data field management. Including data entry, verification and corresponding styles.
When to Use
- Suitable for creating an entity or collecting information.
- When the input data type needs to be checked.
Demos
Validate Only
You can use validateOnly of validateFields to validate the form without showing error UI. This is commonly used to dynamically control the disabled state of the submit button.
getvalueprops-normalize
Form
Props
| Name | Description | Type | Default |
|---|---|---|---|
| disabled | Whether it is disabled | boolean | false |
| footer | The footer content. Commonly used for placing submit buttons. | ReactNode | - |
| form | Form control instance created by Form.useForm(). Automatically created when not provided | FormInstance | - |
| hasFeedback | Whether to show error feedback | boolean | true |
| initialValues | Set value by Form initialization or reset | object | - |
| layout | Layout mode | 'vertical' | 'horizontal' | 'vertical' |
| mode | Support two modes: default and card. | 'default' | 'card' | 'default' |
| name | Form name. Will be the prefix of Field id | string | - |
| onFieldsChange | Trigger when field updated | (changedFields, allFields) => void | - |
| onFinish | Trigger after submitting the form and verifying data successfully | (values) => void | - |
| onFinishFailed | Trigger after submitting the form and verifying data failed | ({ values, errorFields, outOfDate }) => void | - |
| onValuesChange | Trigger when value updated | (changedValues, allValues) => void | - |
| preserve | Keep field value even when field removed | boolean | true |
| requiredMarkStyle | The style of required or optional mark | 'asterisk' | 'text-required' | 'text-optional | 'none' | 'asterisk' |
| validateMessages | Validation prompt template, description see below | ValidateMessages | - |
| validateTrigger | Config field validate trigger | string | string[] | 'onChange' |
FormInstance
| Name | Description | Type |
|---|---|---|
| getFieldError | Get the error messages by the field name | (name: NamePath) => string[] |
| getFieldValue | Get the value by the field name | (name: NamePath) => any |
| getFieldsError | Get the error messages by the fields name. Return as an array | (nameList?: NamePath[]) => FieldError[] |
| getFieldsValue | Get values by a set of field names. Return according to the corresponding structure. Default return mounted field value, but you can use getFieldsValue(true) to get all values | (nameList?: NamePath[], filterFunc?: (meta: { touched: boolean, validating: boolean }) => boolean) => any |
| isFieldTouched | Check if a field has been operated | (name: NamePath) => boolean |
| isFieldsTouched | Check if fields have been operated. Check if all fields is touched when allTouched is true | (nameList?: NamePath[], allTouched?: boolean) => boolean |
| resetFields | Reset fields to initialValues initialValues | (fields?: FieldData[]) => void |
| setFields | Set fields status | (fields: FieldData[]) => void |
| setFieldValue | Set the value by the field name | (name: NamePath, value: any) => void |
| setFieldsValue | Set fields value(Will directly pass to form store. If you do not want to modify passed object, please clone first) | (values) => void |
| submit | Submit the form. It's same as click submit button | () => void |
| validateFields | Validate fields. Set validateOnly to true to validate without updating UI | (nameList?: NamePath[], options?: ValidateOptions) => Promise & (options?: ValidateOptions) => Promise |
validateMessages
Form provides default verification error messages. You can modify the template by configuring validateMessages property. A common usage is to configure localization:
const validateMessages = {
required: "'${name}' is required!",
// ...
};
<Form validateMessages={validateMessages} />;
CSS Variables
| Name | Description | Default |
|---|---|---|
| --border-bottom | Border style of the form bottom | solid 1px var(--adm-border-color) |
| --border-inner | Border style between form items | solid 1px var(--adm-border-color) |
| --border-top | Border style of the form top | solid 1px var(--adm-border-color) |
| --prefix-width | Label width of form items in horizontal layout | 6.8em |
Form.Item
Props
| Name | Description | Type | Default |
|---|---|---|---|
| childElementPosition <Experimental></Experimental> | Position of the widget. | 'normal' | 'right' | 'normal' |
| dependencies | Set the dependency field. See below | NamePath[] | - |
| disabled | Whether it is disabled | boolean | The disabled of parent Form |
| getValueFromEvent | Specify how to get value from event or other onChange arguments | (..args: any[]) => any | - |
| getValueProps | Customize additional props with value. This prop will disable valuePropName | (value) => any | - |
| hasFeedback | Whether to show error feedback | boolean | true |
| help | Prompt text | ReactNode | - |
| helpIcon | Prompt icon | ReactNode | <QuestionCircleOutline /> |
| hidden | Hide this field | boolean | false |
| initialValue | Config sub default value. Form initialValues get higher priority when conflict. | any | - |
| label | Label name | ReactNode | - |
| layout | Layout mode | 'vertical' | 'horizontal' | The layout of parent Form |
| messageVariables | The default validate field info | Record<string, string> | - |
| name | Field name, support array | NamePath | - |
| normalize | Normalize value from component value before passing to Form instance. Do not support async | (value, prevValue, prevValues) => any | - |
| noStyle | No styles, only use field management | boolean | false |
| onClick | The click event of the list item. | (e:React.MouseEvent, widgetRef: React.MutableRefObject<any>) | - |
| preserve | Keep field value even when field removed | boolean | true |
| required | Whether it is required. Note that this property is only used to control the appearance and does not contain validation logic. | boolean | false (if rules is set, it would be judged according to rules) |
| rules | Rules for field validation. | Rule[] | - |
| shouldUpdate | Custom field update logic. See below | boolean | (prevValue, curValue) => boolean | false |
| trigger | When to collect the value of children node. | string | onChange |
| validateFirst | Whether stop validate on first rule of error for this field. Will parallel validate when parallel cofigured | boolean | 'parallel' | false |
| validateTrigger | When to validate the value of children node | string | string[] | onChange |
| valuePropName | Props of children node, for example, the prop of Switch is 'checked'. This prop is an encapsulation of getValueProps, which will be invalid after customizing getValueProps | string | value |
The layout of Form.Item is based on List.Item. So it also supports these props of List.Item:
extra clickable arrow description
A control wrapped by Form.Item with the name property set, the form control will automatically add value (or other properties specified by valuePropName) onChange (or other properties specified by trigger), data synchronization will be taken over by Form. So if you set a name property on Form.Item, make sure its children is a valid ReactElement control and can accept the value' mentioned above and onChange properties (or other properties specified), for example:
<Form.Item name='foo'>
<Input />
</Form.Item>
And the following spellings are wrong:
<Form.Item name='foo'>
<Input />
<div>hello</div>
</Form.Item>
// Wrong: Form.Item's children contain multiple elements
<Form.Item name='foo'>
hello
<Input />
</Form.Item>
// Wrong: Same as above, Form.Item's children contains multiple elements
<Form.Item name='foo'>
<div>
<Input />
</div>
</Form.Item>
// Wrong: Form.Item's children is actually a div, and div cannot accept value and onChange properties
Also, please note:
- You shouldn't use
onChangeon each form control to collect data(useonValuesChangeof Form), but you can still listen toonChange. - You cannot set value for each form control via
valueordefaultValueprop, you should set default value withinitialValuesof Form. Note thatinitialValuescannot be updated bysetStatedynamically, you should usesetFieldsValuein that situation. - You shouldn't call
setStatemanually, please useform.setFieldsValueto change value programmatically.
For example, the following code is wrong:
<Form.Item name='foo'>
<Input
value={myInputValue} // Wrong: Value should not be manually controlled
onChange={(v) => { setMyInputValue(v) }} // Wrong: Although you can listen to the onChange event, you should not maintain your own state here
/>
</Form.Item>
dependencies
Used when there are dependencies between fields. If a field has the dependencies prop, this field will automatically trigger updates and validations when upstream is updated. A common scenario is a user registration form with "password" and "confirm password" fields. The "Confirm Password" validation depends on the "Password" field. After setting dependencies, the "Password" field update will re-trigger the validation of "Check Password".
dependencies shouldn't be used together with shouldUpdate, since it may result in conflicting update logic.
shouldUpdate
Form updates only the modified field-related components for performance optimization purposes by incremental update. In most cases, you only need to write code or do validation with the dependencies property. In some specific cases, such as when a new field option appears with a field value changed, or you just want to keep some area updating by form update, you can modify the update logic of Form.Item via the shouldUpdate.
When shouldUpdate is true, any Form update will cause the Form.Item to be re-rendered. This is very helpful for custom rendering some areas:
<Form.Item shouldUpdate>
{() => {
return <pre>{JSON.stringify(form.getFieldsValue(), null, 2)}</pre>;
}}
</Form.Item>
When shouldUpdate is a function, it will be called by form values update. Providing original values and current value to compare. This is very helpful for rendering additional fields based on values:
<Form.Item shouldUpdate={(prevValues, curValues) => prevValues.additional !== curValues.additional}>
{() => {
return (
<Form.Item name="other">
<Input />
</Form.Item>
);
}}
</Form.Item>
messageVariables
You can modify the default verification information of Form.Item through messageVariables.
<Form>
<Form.Item messageVariables={{ another: 'good' }} label="user">
<Input />
</Form.Item>
<Form.Item messageVariables={{ label: 'good' }} label={<span>user</span>}>
<Input />
</Form.Item>
</Form>
Custom field
Customized or third-party form controls can be used in Form, too. Controls must follow these conventions:
- It has a controlled property
value. - It has event
onChange.
Form.Header
You can use Form.Header to group form items.
Props
| Name | Description | Type | Default |
|---|---|---|---|
| children | Group header. If not passed, it will be rendered as a delimiter with no content. | ReactNode | - |
Form.Subscribe
Props
| Name | Description | Type | Default |
|---|---|---|---|
| children | Render function. | (changedValues: Record<string, any>, form: FormInstance) => ReactNode | - |
| to | Same as the dependencies prop of Form.Item. | NamePath[] | - |
useWatch
Demo
Form.Array <Experimental></Experimental>
Provides array management for fields.
| Name | Description | Type | Default |
|---|---|---|---|
| children | Render function. | (fields: FormArrayField[], operation: FormArrayOperation) => ReactElement[] | - |
| initialValue | Config sub default value. Form initialValues get higher priority when conflict. | any[] | - |
| name | Field name, support array. | NamePath[] | - |
| onAdd | Custom add function. | (operation: FormArrayOperation) => void | - |
| renderAdd | Render the content of add button. | () => ReactNode | - |
| renderHeader | Render the header of each field. | (field: FormArrayField, operation: FormArrayOperation) => ReactNode | - |
FormArrayField
| Name | Description | Type |
|---|---|---|
| index | The array index. | number |
| key | The unique key. | number |
FormArrayOperation
The operation functions for Form.Array.
| Name | Description | Type |
|---|---|---|
| add | Add a field. | (initValue: any) => void |
| remove | Remove a field. | (index: number) => void |
Demo
Some Common Type Definitions
NamePath
string | number | (string | number)[]
ValidateOptions
| Name | Description | Type | Default |
|---|---|---|---|
| validateOnly | Validate only, without updating field UI state (e.g. error message display) | boolean | false |
| recursive | Recursive validate, validate all fields that contain the specified namePath | boolean | false |
| dirty | Only validate fields that have been modified | boolean | false |
FieldData
| Name | Description | Type |
|---|---|---|
| errors | Error messages | string[] |
| name | Field name path | NamePath[] |
| touched | Whether is operated | boolean |
| validating | Whether is in validating | boolean |
| value | Field value | any |
Rule
Rule supports a config object, or a function returning config object:
type Rule = RuleConfig | ((form: FormInstance) => RuleConfig);
| Name | Description | Type |
|---|---|---|
| defaultField | Validate rule for all array elements, valid when type is array | rule |
| enum | Match enum value. You need to set type to enum to enable this | any[] |
| len | Length of string, number, array | number |
| max | type required: max length of string, number, array | number |
| message | Error message. Will auto generate by template if not provided | string |
| min | type required: min length of string, number, array | number |
| pattern | Regex pattern | RegExp |
| required | Required field | boolean |
| transform | Transform value to the rule before validation | (value) => any |
| type | Normally string |number |boolean |url | email. More type to ref here | string |
| validateTrigger | Set validate trigger event. Must be the sub set of validateTrigger in Form.Item | string | string[] |
| validator | Customize validation rule. Accept Promise as return. See example | (rule, value) => Promise |
| warningOnly | Warning only. Not block form submit | boolean |
| whitespace | Failed if only has whitespace, only work with type: 'string' rule | boolean |
FAQ
How does Form.Item work with Picker / DatePicker / CascadePicker?
First, we can render the currently selected value through the children rendering function of Picker. Here we have taken DatePicker as an example, but the same is true for the other two Pickers:
<DatePicker>
{value =>
value ? dayjs(value).format('YYYY-MM-DD') : 'Please select'
}
</DatePicker>
Next we need to deal with the show/hide state of the Picker, which is where the Picker component is the most different and confusing from other form components. If we put the Picker directly in the Form.Item, there is no way to show it to the user, no matter how clicked, it will not let Picker pops up:
<Form.Item
name='birthday'
label='Birthday'
>
<DatePicker>
{value =>
value ? dayjs(value).format('YYYY-MM-DD') : 'Please select'
}
</DatePicker>
</Form.Item>
In most cases, the effect we need to achieve is that clicking on the outer Form.Item will trigger the display of the inner Picker. However, on Form.Item, how can I control the Picker? Maybe you'll want to declare a state yourself to control manually, for example:
const [visible, setVisible] = useState(false)
<Form.Item
name='birthday'
label='Birthday'
onClick={() => {
setVisible(true)
}}
>
<DatePicker
visible={visible}
onClose={() => {
setVisible(false)
}}
>
{value =>
value ? dayjs(value).format('YYYY-MM-DD') : 'Please select'
}
</DatePicker>
</Form.Item>
But this is too cumbersome to write, and if there are multiple Pickers in a form or it is used with Form.Array, it will crash.
So antd-mobile provides a convenient method, you can directly get the ref of the internal children in the onClick event of Form.Item, so we can write:
<Form.Item
name='birthday'
label='Birthday'
onClick={(e, datePickerRef: RefObject<DatePickerRef>) => {
datePickerRef.current?.open() // ⬅️
}}
>
<DatePicker>
{value =>
value ? dayjs(value).format('YYYY-MM-DD') : 'Please select'
}
</DatePicker>
</Form.Item>
Finally, don't forget that the Picker component's confirmation event is onConfirm not onChange, so you need to configure trigger:
<Form.Item
name='birthday'
label='Birthday'
trigger='onConfirm' // ⬅️
onClick={(e, datePickerRef: RefObject<DatePickerRef>) => {
datePickerRef.current?.open()
}}
>
...
</Form.Item>