Ant Design Dynamic Form Example
Ant Design Dynamic Form Example
* Username :
TypeScript JavaScript CSS
Required style
* Password : import React from 'react';
Switch required or optional style with requiredMark .
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
Remember me import { Button, Form, Input } from 'antd';
labelCol: {
import React, { useState } from 'react'; xs: { span: 24 },
import { InfoCircleOutlined } from '@ant-design/icons'; sm: { span: 4 },
Basic Usage import { Button, Form, Input, Radio, Tag } from 'antd'; },
Basic Form data control. Includes layout, initial values, validation and submit. wrapperCol: {
type RequiredMark = boolean | 'optional' | 'customize'; xs: { span: 24 },
sm: { span: 20 },
const customizeRequiredMark = (label: [Link], { required }: { required: boolean }) => ( },
TypeScript JavaScript
<> };
{required ? <Tag color="error">Required</Tag> : <Tag color="warning">optional</Tag>}
import React from 'react';
{label} const formItemLayoutWithOutLabel = {
import { Button, Checkbox, Form, Input } from 'antd';
</> wrapperCol: {
); xs: { span: 24, offset: 0 },
const onFinish = (values: any) => {
[Link]('Success:', values); sm: { span: 20, offset: 4 },
const App: [Link] = () => { },
};
const [form] = [Link](); };
const [requiredMark, setRequiredMarkType] = useState<RequiredMark>('optional');
const onFinishFailed = (errorInfo: any) => {
[Link]('Failed:', errorInfo); const App: [Link] = () => {
const onRequiredTypeChange = ({ requiredMarkValue }: { requiredMarkValue: RequiredMark }) => { const onFinish = (values: any) => {
};
setRequiredMarkType(requiredMarkValue); [Link]('Received values of form:', values);
}; };
type FieldType = {
username?: string;
return ( return (
password?: string;
<Form <Form
remember?: string;
form={form} name="dynamic_form_item"
};
layout="vertical" {...formItemLayoutWithOutLabel}
initialValues={{ requiredMarkValue: requiredMark }} onFinish={onFinish}
const App: [Link] = () => (
onValuesChange={onRequiredTypeChange} style={{ maxWidth: 600 }}
<Form
requiredMark={requiredMark === 'customize' ? customizeRequiredMark : requiredMark} >
name="basic"
> <[Link]
labelCol={{ span: 8 }}
<[Link] label="Required Mark" name="requiredMarkValue"> name="names"
wrapperCol={{ span: 16 }}
<[Link]> rules={[
style={{ maxWidth: 600 }}
<[Link] value>Default</[Link]> {
initialValues={{ remember: true }}
<[Link] value="optional">Optional</[Link]> validator: async (_, names) => {
onFinish={onFinish}
<[Link] value={false}>Hidden</[Link]> if (!names || [Link] < 2) {
onFinishFailed={onFinishFailed}
<[Link] value="customize">Customize</[Link]> return [Link](new Error('At least 2 passengers'));
autoComplete="off"
</[Link]> }
>
</[Link]> },
<[Link]<FieldType>
<[Link] label="Field A" required tooltip="This is a required field"> },
label="Username"
<Input placeholder="input placeholder" /> ]}
name="username"
</[Link]> >
rules={[{ required: true, message: 'Please input your username!' }]}
<[Link] {(fields, { add, remove }, { errors }) => (
>
label="Field B" <>
<Input />
tooltip={{ title: 'Tooltip with customize icon', icon: <InfoCircleOutlined /> }} {[Link]((field, index) => (
</[Link]>
> <[Link]
<Input placeholder="input placeholder" /> {...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
<[Link]<FieldType>
</[Link]> label={index === 0 ? 'Passengers' : ''}
label="Password"
<[Link]> required={false}
name="password"
<Button type="primary">Submit</Button> key={[Link]}
rules={[{ required: true, message: 'Please input your password!' }]}
</[Link]> >
>
</Form> <[Link]
<[Link] />
); {...field}
</[Link]>
}; validateTrigger={['onChange', 'onBlur']}
<[Link]<FieldType> rules={[
export default App; {
name="remember"
valuePropName="checked" required: true,
Hide
wrapperCol={{ offset: 8, span: 16 }} whitespace: true,
<Checkbox>Remember me</Checkbox> },
</[Link]> ]}
Form Size : Small Default Large
noStyle
Submit </[Link]>
Select :
</Button> {[Link] > 1 ? (
</[Link]> <MinusCircleOutlined
TreeSelect : className="dynamic-delete-button"
</Form>
); onClick={() => remove([Link])}
Cascader : />
InputNumber : <[Link]>
<Button
type="dashed"
* Note : Switch :
onClick={() => add()}
style={{ width: '60%' }}
* Gender : Select a option and change input text above Button : Button
icon={<PlusOutlined />}
>
Submit Reset Fill form Add field
</[Link]>
const tailLayout = { type SizeType = Parameters<typeof Form>[0]['size'];
</Form>
wrapperCol: { offset: 8, span: 16 },
);
}; const App: [Link] = () => {
};
const [componentSize, setComponentSize] = useState<SizeType | 'default'>('default');
switch (value) {
case 'male': return (
break; style={{ maxWidth: 600 }} Nest dynamic field need extends field . Pass [Link] to nest item.
default: >
}; <[Link]>
TypeScript JavaScript
<[Link] value="small">Small</[Link]>
const onFinish = (values: any) => { <[Link] value="default">Default</[Link]> import React from 'react';
[Link](values); <[Link] value="large">Large</[Link]> import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
}; </[Link]> import { Button, Form, Input, Space } from 'antd';
</[Link]>
const onReset = () => { <[Link] label="Input"> const onFinish = (values: any) => {
[Link](); <Input /> [Link]('Received values of form:', values);
}; </[Link]> };
<[Link] label="Select">
</Form>
);
}; Item 1
label can wrap
TypeScript JavaScript
labelAlign="left"
Complex Dynamic Form Item
labelWrap
Form methods (Class component) Multiple [Link] nested usage scenarios.
wrapperCol={{ flex: 1 }}
We recommend use [Link] to create data control. If you are using class component, you can get it by ref . colon={false}
>
TypeScript JavaScript
<[Link] label="Normal label" name="username" rules={[{ required: true }]}>
TypeScript JavaScript CSS
<Input /> import React from 'react';
import { Button, Form, Input, Select } from 'antd'; import { Button, Card, Form, Input, Space, Typography } from 'antd';
import type { FormInstance } from 'antd/es/form'; <[Link] label="A super long label text" name="password" rules={[{ required: true }]}>
<Input /> const App: [Link] = () => {
</[Link]> form={form}
}; autoComplete="off"
const onGenderChange = (value: string) => { <div style={{ display: 'flex', rowGap: 16, flexDirection: 'column' }}>
break; <CloseOutlined
default: />
break; }
const onFinish = (values: any) => { import { Button, Form, Input, message, Space } from 'antd'; </[Link]>
[Link](values);
}; const App: [Link] = () => { {/* Nest [Link] */}
const [form] = [Link](); <[Link] label="List">
}; [Link]('Submit success!'); <div style={{ display: 'flex', flexDirection: 'column', rowGap: 16 }}>
}; {[Link]((subField) => (
[Link]?.setFieldsValue({ note: 'Hello world!', gender: 'male' }); const onFinishFailed = () => { <[Link] noStyle name={[[Link], 'first']}>
onFinish={onFinish} }; [Link]([Link]);
<[Link] name="gender" label="Gender" rules={[{ required: true }]}> onFinish={onFinish} + Add Sub Item
onChange={onGenderChange} > )}
<Option value="female">female</Option> rules={[{ required: true }, { type: 'url', warningOnly: true }, { type: 'string', min: 6 }]} ))}
</Select> <Input placeholder="input placeholder" /> <Button type="dashed" onClick={() => add()} block>
<[Link] name="customizeGender" label="Customize Gender" rules={[{ required: true }]}> Fill {() => (
} </Form> )}
</[Link]> ); </[Link]>
</Button>
Hide
<Button htmlType="button" onClick={onReset}> export default App;
Reset
</Button> Hide
useWatch helps watch the field change and only re-render for the value change. API Ref.
Introduction :
const Demo: [Link] = () => { name prop support nest data structure. Customize validate message template with validateMessages or message . Ref here about message template.
TypeScript JavaScript
return (
Form Layout
<> import React from 'react';
There are three layout for form: horizontal , vertical , inline .
<Form form={form} layout="vertical" autoComplete="off"> import { Button, Form, Input, InputNumber } from 'antd';
); },
const onFormLayoutChange = ({ layout }: { layout: LayoutType }) => { }; number: {
setFormLayout(layout); range: '${label} must be between ${min} and ${max}',
}; export default Demo; },
};
const formItemLayout = Hide /* eslint-enable no-template-curly-in-string */
formLayout === 'horizontal' ? { labelCol: { span: 4 }, wrapperCol: { span: 14 } } : null;
Hide
<[Link]
Form disabled
hasFeedback
Checkbox : Checkbox
label="Field B"
Username : Please input Need Help?
name="field_b"
Radio : Apple Pear
validateDebounce={1000}
<Input placeholder="Validate required debounce after 1s" /> BirthDate : Input birth year Input birth month
Select : </[Link]>
Submit
TreeSelect : <[Link]
hasFeedback
validateFirst This demo shows how to use [Link] with multiple controls. <[Link] name="field" /> will only bind the control(Input/Select) which is the only children of it. Imagine this case: you added some text description after the Input, then you have to wrap the Input by an extra <[Link] name="field"> . style property of [Link] could be useful to modify the nested form item layout, or use <[Link] noStyle /> to turn it into a pure form-binded component(like getFieldDecorator in 3.x).
DatePicker : Select date
rules={[{ max: 6 }, { max: 3, message: 'Continue input to exceed 6 chars' }]}
>
RangePicker : Start date End date
<Input placeholder="Validate one by one" />
TypeScript JavaScript
</[Link]>
InputNumber :
</Form> import React from 'react';
); import { Button, Form, Input, Select, Space, Tooltip, Typography } from 'antd';
TextArea :
Hide
const onFinish = (values: any) => {
* Name
Upload :
const App: [Link] = () => (
<Form
Upload
name="complex-form"
* Age
onFinish={onFinish}
labelCol={{ span: 8 }}
Button : Button
wrapperCol={{ span: 16 }}
<[Link] label="Username">
<Space>
Validate Only
<[Link]
Form disabled Dynamic adjust submit button's disabled status by validateOnly of validateFields . name="username"
Set component disabled, only works for antd components.
noStyle
<[Link] label="Input">
<Input />
Last Name
</[Link]>
<[Link] label="Select">
<Select>
<[Link] value="demo">Demo</[Link]> Age
</Select>
</[Link]>
<[Link] label="TreeSelect">
Submit
<TreeSelect
treeData={[
{ title: 'Light', value: 'light', children: [{ title: 'Bamboo', value: 'bamboo' }] }, Path Prefix
]}
In some scenarios, you may want to set a prefix for some fields consistently. You can achieve this effect with HOC.
/>
</[Link]>
<[Link] label="Cascader">
options={[
import React from 'react';
{
import { Form, Input, Button } from 'antd';
value: 'zhejiang',
import type { FormItemProps } from 'antd';
label: 'Zhejiang',
children: [
const MyFormItemContext = [Link]<(string | number)[]>([]);
{
value: 'hangzhou',
interface MyFormItemGroupProps {
label: 'Hangzhou',
prefix: string | number | (string | number)[];
},
children: [Link];
],
}
},
]}
function toArr(str: string | number | (string | number)[]): (string | number)[] {
/>
return [Link](str) ? str : [str];
</[Link]>
}
<[Link] label="DatePicker">
<DatePicker />
const MyFormItemGroup = ({ prefix, children }: MyFormItemGroupProps) => {
</[Link]>
const prefixPath = [Link](MyFormItemContext);
<[Link] label="RangePicker">
const concatPath = [Link](() => [...prefixPath, ...toArr(prefix)], [prefixPath, prefix]);
<RangePicker />
</[Link]>
return <[Link] value={concatPath}>{children}</[Link]>;
<[Link] label="InputNumber">
};
<InputNumber />
</[Link]>
const MyFormItem = ({ name, ...props }: FormItemProps) => {
<[Link] label="TextArea">
const prefixPath = [Link](MyFormItemContext);
<TextArea rows={4} />
const concatName = name !== undefined ? [...prefixPath, ...toArr(name)] : undefined;
</[Link]>
<Input />
export default () => <FormDisabledDemo />;
</MyFormItem>
</MyFormItemGroup>
Hide
Submit
</Button>
</Form>
);
};
Hide
* Password :
Customized Form Controls
Customized or third-party form controls can be used in Form, too. Controls must follow these conventions:
* Confirm Password :
It has a controlled property value or other name which is equal to the value of valuePropName .
It has event onChange or an event which name is equal to the value of trigger .
* Nickname :
TypeScript JavaScript
* Phone Number : +86
import React, { useState } from 'react';
interface PriceValue {
0 / 100
number?: number;
}
Captcha : Get captcha
interface PriceInputProps { We must make sure that your are a human.
value?: PriceValue;
onChange?: (value: PriceValue) => void; I have read the agreement
}
Register
const PriceInput: [Link]<PriceInputProps> = ({ value = {}, onChange }) => {
const [number, setNumber] = useState(0);
const [currency, setCurrency] = useState<Currency>('rmb');
Registration
const triggerChange = (changedValue: { number?: number; currency?: Currency }) => { Fill in this form to create a new account for you.
TypeScript JavaScript
const onNumberChange = (e: [Link]<HTMLInputElement>) => {
const newNumber = parseInt([Link] || '0', 10); import React, { useState } from 'react';
if ([Link](number)) { import type { CascaderProps } from 'antd';
return; import {
} AutoComplete,
if (!('number' in value)) { Button,
setNumber(newNumber); Cascader,
} Checkbox,
triggerChange({ number: newNumber }); Col,
}; Form,
Input,
const onCurrencyChange = (newCurrency: Currency) => { InputNumber,
if (!('currency' in value)) { Row,
setCurrency(newCurrency); Select,
} } from 'antd';
triggerChange({ currency: newCurrency });
],
const App: [Link] = () => { },
const onFinish = (values: any) => { ],
[Link]('Received values from form: ', values); },
}; {
value: 'jiangsu',
const checkPrice = (_: any, value: { number: number }) => { label: 'Jiangsu',
if ([Link] > 0) { children: [
return [Link](); {
} value: 'nanjing',
return [Link](new Error('Price must be greater than zero!')); label: 'Nanjing',
}; children: [
{
return ( value: 'zhonghuamen',
<Form label: 'Zhong Hua Men',
name="customized_form_controls" },
layout="inline" ],
onFinish={onFinish} },
initialValues={{ ],
price: { },
number: 0, ];
currency: 'rmb',
}, const formItemLayout = {
}} labelCol: {
> xs: { span: 24 },
<[Link] name="price" label="Price" rules={[{ validator: checkPrice }]}> sm: { span: 8 },
<PriceInput /> },
</[Link]> wrapperCol: {
<[Link]> xs: { span: 24 },
<Button type="primary" htmlType="submit"> sm: { span: 16 },
Submit },
</Button> };
</[Link]>
</Form> const tailFormItemLayout = {
); wrapperCol: {
}; xs: {
span: 24,
export default App; offset: 0,
},
Hide
sm: {
span: 16,
offset: 8,
},
* Username : Ant Design
},
};
[
{
const App: [Link] = () => {
"name": [
"username" const [form] = [Link]();
],
"value": "Ant Design" const onFinish = (values: any) => {
}
[Link]('Received values of form: ', values);
]
};
const prefixSelector = (
Store Form Data into Upper Component
<[Link] name="prefix" noStyle>
We can store form data into upper component or Redux or dva by using onFieldsChange and fields , see more at this rc-field-form demo. <Select style={{ width: 70 }}>
Note: Save Form data globally is not a good practice. You should avoid this if not necessary. <Option value="86">+86</Option>
<Option value="87">+87</Option>
</Select>
</[Link]>
TypeScript JavaScript
);
<Option value="USD">$</Option>
touched?: boolean; );
validating?: boolean;
errors?: string[]; const [autoCompleteResult, setAutoCompleteResult] = useState<string[]>([]);
}
const onWebsiteChange = (value: string) => {
<Form
onChange(allFields);
}} return (
> <Form
<[Link] {...formItemLayout}
name="username" form={form}
label="Username" name="register"
</[Link]> scrollToFirstError
</Form> >
); <[Link]
name="email"
const [fields, setFields] = useState<FieldData[]>([{ name: ['username'], value: 'Ant Design' }]); rules={[
{
<CustomizedForm },
fields={fields} {
setFields(newFields);
}}
/>
<Paragraph style={{ maxWidth: 440, marginTop: 24 }}>
</>
);
};
Hide
* Group Name :
Use [Link] to process data between forms. In this case, submit button is in the Modal which is out of Form. You can use [Link] to submit form. Besides, we recommend native <Button htmlType="submit" /> to submit a form.
TypeScript JavaScript
import { Avatar, Button, Form, Input, InputNumber, Modal, Space, Typography } from 'antd';
import type { FormInstance } from 'antd/es/form';
const layout = {
labelCol: { span: 8 },
wrapperCol: { span: 16 },
};
const tailLayout = {
wrapperCol: { offset: 8, span: 16 },
};
interface UserType {
name: string;
age: string;
}
interface ModalFormProps {
open: boolean;
}, [open]);
const prevOpen = [Link];
useEffect(() => {
if (!open && prevOpen) {
[Link]();
}
}, [form, prevOpen, open]);
};
useResetFormOnCloseModal({
form,
open,
});
return (
<Input />
</[Link]>
<[Link] name="age" label="User Age" rules={[{ required: true }]}>
<InputNumber />
</[Link]>
</Form>
</Modal>
);
};
setOpen(false);
};
return (
<[Link]
onFormFinish={(name, { values, forms }) => {
if (name === 'userForm') {
}}
>
<Form {...layout} name="basicForm" onFinish={onFinish} style={{ maxWidth: 600 }}>
</[Link]>
{/* Create a hidden field to make Form instance record this */}
<[Link]
label="User List"
shouldUpdate={(prevValues, curValues) => [Link] !== [Link]}
>
{({ getFieldValue }) => {
const users: UserType[] = getFieldValue('users') || [];
return [Link] ? (
<ul>
{[Link]((user) => (
</li>
))}
</ul>
) : (
<[Link] className="ant-form-text" type="secondary">
}}
</[Link]> Hide
<[Link] {...tailLayout}>
<Button htmlType="submit" type="primary">
Submit
</Button>
<Button htmlType="button" style={{ margin: '0 8px' }} onClick={showUserModal}>
Add User
</Button>
</[Link]>
</Form>
</[Link]>
);
};
Hide
TypeScript JavaScript
useEffect(() => {
setClientReady(true);
}, []);
[Link]('Finish:', values);
};
return (
<Form form={form} name="horizontal_login" layout="inline" onFinish={onFinish}>
<[Link]
name="username"
rules={[{ required: true, message: 'Please input your username!' }]}
>
<Input prefix={<UserOutlined className="site-form-item-icon" />} placeholder="Username" />
</[Link]>
<[Link]
name="password"
placeholder="Password"
/>
</[Link]>
<[Link] shouldUpdate>
{() => (
<Button
type="primary"
htmlType="submit"
disabled={
!clientReady ||
 ||
Log in
</Button>
)}
</[Link]>
</Form>
);
};
Hide
Username
Password
Log in
Or register now!
Login Form
return (
<Form
name="normal_login"
className="login-form"
<[Link]
name="username"
</[Link]>
<[Link]
name="password"
<Input
prefix={<LockOutlined className="site-form-item-icon" />}
type="password"
placeholder="Password"
/>
</[Link]>
<[Link]>
<[Link] name="remember" valuePropName="checked" noStyle>
<Checkbox>Remember me</Checkbox>
</[Link]>
</a>
</[Link]>
<[Link]>
<Button type="primary" htmlType="submit" className="login-form-button">
Log in
</Button>
Or <a href="">register now!</a>
</[Link]>
</Form>
);
};
Hide