--- localeCode: en-US order: 24 category: Input title: Form subTitle: Form icon: doc-form dir: column --- ## Form - **Rerender on demand**, avoids unnecessary full-volume rendering, higher performance - Easy to use, **simple structure**, avoids unnecessary hierarchical nesting - Perfect accessibility support - FormState / FieldState can also be easily obtained from outside the Form Provides an external method to operate inside the form: formApi / fieldApi - Support for encapsulating custom components into form controls, and you can quickly access your team's components through the extension mechanism provided by Form (through `withField` HOC) - Support Form level / Field level assignment, verification (synchronous / asynchronous) ## Field Semi encapsulates all form field component (Input、Select、Checkbox、DatePicker etc.) with `withField` once. Taking over their data flow (`props.value` & `props.onChange`) When in use, you need to import from the Form (note: only the control imported from the Form has data synchronization) ### Supported Field Component - `Input`, `InputNumber`, `TextArea`, `Select`, `Checkbox`, `Radio`, `RadioGroup`, `Switch`, `DatePicker`, `TimePicker`, `Slider`, `InputGroup`, `TreeSelect`, `Cascader`, `Rating`, `AutoComplete`, `Upload`, `Label`, `ErrorMessage`, `Section`、`TagInput` All mounted under Form and declared directly in `` and `` when used. ```javascript import { Form } from '@douyinfe/semi-ui'; const FormInput = Form.Input; const FormSelect = Form.Select; const Option = FormSelect.Option; ``` The Field level component provided by Form, its `value` (or other properties specified by `valueKey`), onChange (or other callback functions specified by `onKeyChangeFnName`) Properties are hijacked by Form, so
1. No longer need to manually bind the onChange event and update the value as controled component. But you can continue to listen onChange events for the latest values if you want
2. You cannot set the state of component with attributes such as `value`, `defaultValue`, `checked`, `defaultChecked`, etc. The default value can be set by Field's `initValue` or Form's `unitValues`
3. You should not modify the value of Form State directly, all changes to the data in the Form should be done by providing `formApi`, `fieldApi`
## Demos ### Various ways to declare form Semi Form supports multiple writing at the same time. #### Basic Usage Add `field` property to each field component. You can also set `label` properties for each field, by default is the same as field `label` can be passed in a string directly, or declared in the form of an object, configure `extra`, `required`, `optional` and other attributes to deal with more complex scenarios The field attribute is required props ```jsx live=true dir="column" import React from 'react'; import { Form, Tooltip } from '@douyinfe/semi-ui'; import { IconHelpCircle } from '@douyinfe/semi-icons'; () => (
}} style={{ width: 176 }} /> ); ``` #### Other declaration methods When you need to get `formState`, `formApi`, `values`, etc. directly inside the Form structure, you can use the following writing #### Via render props ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => (
( <> Admin User Guest {JSON.stringify(formState)} )} layout='horizontal'> ); ``` #### Via children function declare children as a function that returns all field components ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => (
{ ({ formState, values, formApi }) => ( <> Admin User Guest {JSON.stringify(formState)} ) } ); ``` #### Via props.component Pass the entire internal structure directly in the form through `component` attribute. ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; class Demo extends React.Component { constructor() { super(); } render() { const fields = ({ formState, formApi, values }) => ( <> {JSON.stringify(formState)} ); return
; } } ``` ### All supported field components ```jsx live=true dir="column" import React from 'react'; import { Form, Col, Row, Button } from '@douyinfe/semi-ui'; import { IconUpload } from '@douyinfe/semi-icons'; class BasicDemoWithInit extends React.Component { constructor() { super(); this.state = { initValues: { name: 'semi', business: ['ulikeCam'], role: 'ued', switch: true, files: [ { uid: '1', name: 'vigo.png', status: 'success', size: '130KB', preview: true, url: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/vigo.png' }, { uid: '2', name: 'resso.jpeg', status: 'validateFail', size: '222KB', percent: 50, preview: true, fileInstance: new File([new ArrayBuffer(2048)], 'resso.jpeg', { type: 'image/jpeg' }), url: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/Resso.png' }, { uid: '3', name: 'dy.jpeg', status: 'uploading', size: '222KB', percent: 50, preview: true, fileInstance: new File([new ArrayBuffer(2048)], 'dy.jpeg', { type: 'image/jpeg' }), url: 'https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/dy.png' } ] } }; } render() { const { Input, InputNumber, AutoComplete, Select, TreeSelect, Cascader, DatePicker, TimePicker, TextArea, CheckboxGroup, Checkbox, RadioGroup, Radio, Slider, Rating, Switch, TagInput, Section } = Form; const { initValues } = this.state; const plainOptions = ['A', 'B', 'C']; const style = { width: '90%' }; const treeData = [ { label: 'Asia', value: 'Asia', key: '0', children: [ { label: 'China', value: 'China', key: '0-0', children: [ { label: 'Beijing', value: 'Beijing', key: '0-0-0', }, { label: 'Shanghai', value: 'Shanghai', key: '0-0-1', }, ], }, ], }, { label: 'North America', value: 'North America', key: '1', } ]; return ( console.log(v)} >
) } ); ``` ### Form layout - Vertical Layout: Arrange each field vertically (By default) Semi Design recommends a vertical layout. ```jsx live=true dir="column" import React from 'react'; import { Form, Button, Toast } from '@douyinfe/semi-ui'; () => { const handleSubmit = (values) => { console.log(values); Toast.info('Submit Success'); }; return (
handleSubmit(values)} style={{ width: 400 }}> {({ formState, values, formApi }) => ( <> I have read and agree to the terms of service

Or

)}
); }; ``` - Horizontal Layout: Arrange each field horizontally You can use the horizontal layout by setting `layout='horizontal'` ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => (
); ``` - Label Position, Label Align You can control the position of the label in the Field and the direction of text alignment by setting `labelPosition`, `labelAlign` ```jsx live=true dir="column" import React from 'react'; import { Form, Select, Checkbox, Radio } from '@douyinfe/semi-ui'; class BasicDemo extends React.Component { constructor(props) { super(props); this.state = { labelPosition: 'left', labelAlign: 'left', }; } render() { const { labelPosition, labelAlign } = this.state; const labelWidth = 120; return ( <>
this.setState(values)} >
value === 'semi', message: 'not semi' } ]} /> mike jane kate admin user guest root man woman ); } } ``` - A more complex layout. You can also combine the `Row` and `Col` provided by the `Grid` to arrange the form structure as you want. ```jsx live=true dir="column" import React from 'react'; import { Form, Row, Col } from '@douyinfe/semi-ui'; () => (
value === 'semi', message: 'not semi' } ]} /> Semi Vigo BussVideo value === 'semi', message: 'not semi' } ]} /> Semi Vigo BussVideo Quality Assurance Software Engineer Product Manager Designer
); ``` ### wrapper Col / label Col When you need to set a uniform layout for all Fields in a Form, you can set `wrapperCol` and `labelCol` on the `Form` to quickly generate the layout. No need to manually use `Row`, `Col` manual layout. `wrapperCol`,`labelCol`Property Configuration Reference [Col components](/en-US/basic/grid#Col) ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => (
Quality Assurance Software Engineer Product Manager Designer ); ``` ### Remove automatically added Label Form will automatically insert `Label` for Field control. If you don't need to automatically insert the Label module, you can turn off the automatic label insertion function by setting `noLabel=true` in the Field (at this time, the Field still has the ability to automatically display ErrorMessage, so the DOM structure is still different from the original component) If you want to keep the DOM structure consistent with the original component, you can use `pure=true`. At this time, the DOM structure will not change except that the data flow is taken over (you need to be responsible for the rendering of ErrorMessage, and it cannot be used by formProps.wrapperCol property impact) ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => (
console.log(values)} style={{ width: 400 }}> val !== 'semi' ? 'not semi' : '' } placeholder='Type your name' /> ); ``` ### Embedded Label A Label can be inlined in a field control by setting labelPosition to `inset`. Components currently supporting this feature include `Input`, `InputNumber`, `DatePicker`, `TimePicker`, `Select`, `TreeSelect`, `Cascader`, `TagInput` ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => (
operate rd pm ued ); ``` ### Export Label, ErrorMessage use When the built-in Label and ErrorMessage layout does not meet the business requirements, you need to combine the positions yourself, but you want to use the default styles of Label and ErrorMessage directly. you can import them from the `Form` module, and combine `Form.Label` / `Form.ErrorMessage` by yourself. For details of their API, refer to [Label](#Form.Label) / [ErrorMessage](#Form.ErrorMessage) ```jsx import { Form } from '@douyinfe/semi-ui'; const { Label, ErrorMessage } = Form; ``` ### Use Form.Slot When your custom component needs to maintain the same layout style as the Field component, you can place your custom component in `Form.Slot` `labelWidth`, `labelAlign`, `wrapperCol`, `labelCol` set on the Form component automatically acts on `Form.Slot` For the Slot property configuration, refer to [Form.Slot](#Form.Slot) ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; class AssistComponent extends React.Component { render() { return (
console.log(v)} onSubmit={v=>console.log(v)} style={{ width: 600 }} labelPosition='left' labelWidth={100} >
{`I'm Semi Form SlotA, a custom ReactNode`}
{`I'm Semi Form SlotA, i have different labelWidth and textAlign.`}
);} } ``` ### Use helpText、extraText set prompt information You can place custom prompt information through `helpText`, and display it in the same block as the verification information (error). When both have values, the verification information will be displayed first. Additional prompt information can be placed through `extraText`. When the error message and prompt text need to appear at the same time, this configuration can be used. It is always displayed and located after helpText/error When `validateStatus` is passed in, the UI style corresponding to the value of validateStatus will be displayed first. If not passed in, the internal verification status of the field shall prevail. ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => { const [helpText, setHelpText] = useState(''); const [validateStatus, setValidateStatus] = useState('default'); const formRef = useRef(); const validate = (val, values) => { if (!val) { setValidateStatus('error'); return Password can not be blank; } else if (val && val.length <= 3) { setValidateStatus('warning'); setHelpText(Password Strength: Weak); // show helpText return ''; // validate pass } else { setHelpText(''); setValidateStatus('success'); return ''; } }; const random = () => { let pw = (Math.random() * 100000).toString().slice(0, 5); formRef.current.formApi.setValue('Password', pw); formRef.current.formApi.setError('Password', ''); setHelpText(''); setValidateStatus('success'); }; return (
console.log('submit success')} onSubmitFail={(errors) => console.log(errors)} > Don't have a suitable password? Click to generate a random } >
); }; ``` By configuring `extraTextPosition`, you can control the display position of extraText. Optional values `bottom`, `middle` For example, when you want to display the extraText prompt information between the Label and Field component. This attribute can be configured uniformly on the Form or individually on each Field. When passing in at the same time, the configuration of the Field shall prevail. ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; () => { const options = [ { label: 'Lark Notification', value: 'lark' }, { label: 'Email Notification', value: 'email' }, { label: 'Banner Notification', value: 'notification' } ]; const notifyText = "When unchecked, the default is a red dot reminder, and the message enters the recipient's message list by default. For important notifications, you can check the corresponding notification methods at the same time"; const forceText = "For dialog notifications, you can specify that the message must wait for a specified amount of time before it can be marked as read."; return (
); }; ``` ### Using Input Group When you need to combine some fields to use, you can use `Form.InputGroup` to wrap them. In Semi Form, when you using field components like `Form.Input`、`Form.Select`, Form will insert Label module automatically for them. But usually, in`InputGroup` you only need a Label belonging to the entire Group. You can set the label property in the `InputGroup` to insert a Label belonging to the Group `label` configurable properties, see [Label](#Form.Label) ```jsx live=true dir="column" import React from 'react'; import { Form, Button } from '@douyinfe/semi-ui'; () => (
console.log(values)} labelPosition='top' style={{ width: 400 }}> PhoneNumber), required: true }} labelPosition='top'> USA +1 China +86 Japan+81
); ``` ### Form in the Modal pop-up layer You can place the Form in Modal and load it as a popup. When submitting, use `formApi.validate()` to centrally verify the Field ```jsx live=true dir="column" import React from 'react'; import { Form, Modal, Select, Button, Row, Col } from '@douyinfe/semi-ui'; class ModalFormDemo extends React.Component { constructor(props) { super(props); this.state = { visible: false, }; this.showDialog = this.showDialog.bind(this); this.handleOk = this.handleOk.bind(this); this.handleCancel = this.handleCancel.bind(this); this.getFormApi = this.getFormApi.bind(this); } showDialog() { this.setState({ visible: true }); } handleOk() { this.formApi.validate() .then((values) => { console.log(values); }) .catch((errors) => { console.log(errors); }); } handleCancel() { this.setState({ visible: false }); } getFormApi(formApi) { this.formApi = formApi; } render(){ const { visible } = this.state; let message = 'Required'; return ( <>
China USA Europe Japan
); } } ``` ### Configure initial values and verification rules - You can configure check rules for each Field through `rules` The verification library inside the Form is based on `async-validator`, and more configuration rules can be found in its [official documentation](https://github.com/yiminghe/async-validator) - You can uniformly set the initial value for the entire form through the `initValues` of form, or you can set the initial value through `initValue` in each field (the latter has a higher priority) ```jsx live=true dir="column" import React from 'react'; import { Form } from '@douyinfe/semi-ui'; class BasicDemoWithInit extends React.Component { constructor() { super(); this.state = { initValues: { name: 'semi', role: 'rd' } }; this.getFormApi = this.getFormApi.bind(this); } getFormApi(formApi) { this.formApi = formApi; } render() { const { Select, Input } = Form; const style = { width: '100%' }; return (
value === 'muji', message: 'not muji' } ]} />
); } } ``` ### Custom Validate (Form Level) You can set a custom validation function validateFields for the `form` as a whole, which will be called when submit #### Synchronous Validate When validate success, you should return an empty string. When validate fails, you should return the error message (Object, key is fieldName, value is the corresponding error message) ```jsx live=true dir="column" import React from 'react'; import { Form, Button } from '@douyinfe/semi-ui'; class FormLevelValidateSync extends React.Component { constructor() { super(); this.syncValidate = this.syncValidate.bind(this); } syncValidate(values) { const errors = {}; if (values.name !== 'mike') { errors.name = 'you must name mike'; } if (values.sex !== 'female') { errors.sex = 'must be woman'; } errors.familyName = [ { before: 'before errror balabala ', after: 'after error balabala' }, 'familyName[1] error balabala' ]; return errors; } render() { return (
); } } ``` #### Asynchronous Validate For asynchronous validation, you should return a promise. In promise.then() you need to return the corresponding error message. ```jsx live=true dir="column" import React from 'react'; import { Form, Button } from '@douyinfe/semi-ui'; class FormLevelValidateAsync extends React.Component { constructor() { super(); this.asyncValidate = this.asyncValidate.bind(this); } asyncValidate(values) { const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); return sleep(2000).then(() => { let errors = {}; if (values.name !== 'mike') { errors.name = 'you must name mike'; } if (values.sex !== 'female') { errors.sex = 'sex not valid'; } return errors; }); } render() { return (
); } } ``` ### Custom Validate (Field Level) You can specify a custom validation function for field. Supports synchronous and asynchronous validation (by returning promises) ```jsx live=true dir="column" import React from 'react'; import { Form, Button } from '@douyinfe/semi-ui'; class FieldLevelValidateDemo extends React.Component { constructor() { super(); this.validateName = this.validateName.bind(this); this.asyncValidate = this.asyncValidate.bind(this); } validateName(val) { if (!val) { return '【sync】can\'t be empty'; } else if (val.length <= 5) { return '【sync】must more than 5'; } return ''; } asyncValidate(val, values) { const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); return sleep(2000).then(() => { if (!val) { return '【async】can\'t be empty'; } else if (val.length <= 5) { return '【async】must more than 5'; } else { return ''; } }); } render() { return (
); } } ``` ### Manually Trigger specified validation When you want to manually trigger the validation of some specific Field, you can do it through `formApi.validate`. When no parameters are passed in, all Fields are checked by default. When parameters are passed in, the parameters specified shall prevail ```jsx live=true dir="column" import React from 'react'; import { Form, Button, Space } from '@douyinfe/semi-ui'; class PartValidAndResetDemo extends React.Component { constructor() { super(); this.validate = this.validate.bind(this); this.getFormApi = this.getFormApi.bind(this); this.validatePartial = this.validatePartial.bind(this); this.resetPartial = this.resetPartial.bind(this); } getFormApi(formApi) { this.formApi = formApi; } validate(val) { if (!val) { return 'can\'t be empty'; } else if (val.length <= 5) { return (i am incoming reactNode); } return; } validatePartial(type) { let scope = this.formApi.getValue('validateScope'); !scope ? scope = [] : null; type === 'all' ? scope = ['a', 'b', 'c', 'd', 'b.name'] : null; this.formApi.validate(scope) .then(values => { console.log(values); Toast.success('pass'); }).catch(error => { Toast.error('error'); console.log(error); }); } resetPartial() { let scope = this.formApi.getValue('resetScope'); this.formApi.reset(scope); } render() { let options = ['a', 'b', 'c', 'd', 'b.name'].map(item => ({ label: item, value: item })); return (
{ ({ formState, values, formApi }) => ( <>
) }
); } } ``` ### Linkage Fields You can achieve the linkage between Fields by listening to the `onChange` of Field and then using formApi to make modifications. ```jsx live=true dir="column" import React from 'react'; import { Form, Button, Row } from '@douyinfe/semi-ui'; class LinkFieldForm extends React.Component { constructor() { super(); this.getFormApi = this.getFormApi.bind(this); this.handleSelectChange = this.handleSelectChange.bind(this); } handleSelectChange(value) { let text = value === 'male' ? 'Hi male' : 'Hi female!'; this.formApi.setValue('Note', text); } getFormApi(formApi) { this.formApi = formApi; } render() { return (
console.log(values) } style={{ width: 300 }}> female male ); } } ``` ### Dynamic form #### Dynamically add and delete fields ```jsx live=true dir="column" import React from 'react'; import { Form, Button } from '@douyinfe/semi-ui'; () => (
{({ formState }) => ( yes no {formState.values.isAnchor === 'yes' ? ( ) : null} )}
); ``` #### Add or delete form items dynamically - by use ArrayField For array items that are dynamically added or deleted, we provide the `ArrayField` component to simplify the operation of add / remove For the detailed API of ArrayField, please refer to [ArrayField Props](#arrayfield-props) below Note: The initValue type of ArrayField must be an array ```jsx live=true dir="column" import React from 'react'; import { ArrayField, TextArea, Form, Button, useFormState } from '@douyinfe/semi-ui'; import { IconPlusCircle, IconMinusCircle } from '@douyinfe/semi-icons'; class ArrayFieldDemo extends React.Component { constructor() { super(); this.state = { data: [ { name: 'Semi D2C', role: 'Engineer' }, { name: 'Semi C2D', role: 'Designer' }, ] }; } render() { let { data } = this.state; const ComponentUsingFormState = () => { const formState = useFormState(); return (