<FormControl><FormControl.Label>Name</FormControl.Label><TextInput /></FormControl>
const DifferentInputs = () => {const [tokens, setTokens] = React.useState([{text: 'zero', id: 0},{text: 'one', id: 1},{text: 'two', id: 2},])const onTokenRemove = tokenId => {setTokens(tokens.filter(token => token.id !== tokenId))}return (<Box display="grid" gridGap={3}><FormControl><FormControl.Label>TextInputWithTokens</FormControl.Label><TextInputWithTokens onTokenRemove={onTokenRemove} tokens={tokens} /></FormControl><FormControl><FormControl.Label>Autocomplete</FormControl.Label><Autocomplete><Autocomplete.Input block /><Autocomplete.Overlay><Autocomplete.Menuitems={[{text: 'css', id: 0},{text: 'css-in-js', id: 1},{text: 'styled-system', id: 2},{text: 'javascript', id: 3},{text: 'typescript', id: 4},{text: 'react', id: 5},{text: 'design-systems', id: 6},]}selectedItemIds={[]}/></Autocomplete.Overlay></Autocomplete></FormControl><FormControl><FormControl.Label>Select</FormControl.Label><Select><Select.Option value="figma">Figma</Select.Option><Select.Option value="css">Primer CSS</Select.Option><Select.Option value="prc">Primer React components</Select.Option><Select.Option value="pvc">Primer ViewComponents</Select.Option></Select></FormControl><FormControl><FormControl.Label>Textarea</FormControl.Label><Textarea /></FormControl></Box>)}render(DifferentInputs)
When rendering an input other than a form component from Primer, you must manually pass the attributes that make the form control accessible:
FormControl.Label
should be associated with the text input by using htmlFor
aria-describedby
aria-describedby
aria-describedby
. Example: aria-describedby="caption-id validation-id"
aria-invalid={true}
should be passed to the input.disabled
should be passed.required
should be passed.When rendering a custom checkbox or radio component, you must also pass layout="horizontal"
to the FormControl
component.
const CustomTextInput = props => <input type="text" {...props} />const CustomCheckboxInput = props => <input type="checkbox" {...props} />const FormControlWithCustomInput = () => {const [value, setValue] = React.useState('mona lisa')const [validationResult, setValidationResult] = React.useState()const doesValueContainSpaces = inputValue => /\s/g.test(inputValue)const handleInputChange = e => {setValue(e.currentTarget.value)}React.useEffect(() => {if (doesValueContainSpaces(value)) {setValidationResult('noSpaces')} else if (value) {setValidationResult('validName')}}, [value])return (<Box display="grid" gridGap={3}><FormControl><FormControl.Label htmlFor="custom-input">GitHub handle</FormControl.Label><CustomTextInputid="custom-input"aria-describedby="custom-input-caption custom-input-validation"aria-invalid={validationResult === 'noSpaces'}onChange={handleInputChange}/>{validationResult === 'noSpaces' && (<FormControl.Validation id="custom-input-validation" variant="error">GitHub handles cannot contain spaces</FormControl.Validation>)}{validationResult === 'validName' && (<FormControl.Validation id="custom-input-validation" variant="success">Valid name</FormControl.Validation>)}<FormControl.Caption id="custom-input-caption">With or without "@". For example "monalisa" or "@monalisa"</FormControl.Caption></FormControl><CheckboxGroup><CheckboxGroup.Label>Checkboxes</CheckboxGroup.Label><FormControl layout="horizontal"><CustomCheckboxInput id="custom-checkbox-one" value="checkOne" /><FormControl.Label htmlFor="custom-checkbox-one">Checkbox one</FormControl.Label><FormControl.Caption id="custom-checkbox-one-caption">Hint text for checkbox one</FormControl.Caption></FormControl><FormControl layout="horizontal"><CustomCheckboxInput id="custom-checkbox-two" value="checkTwo" /><FormControl.Label htmlFor="custom-checkbox-two">Checkbox two</FormControl.Label><FormControl.Caption id="custom-checkbox-two-caption">Hint text for checkbox two</FormControl.Caption></FormControl></CheckboxGroup></Box>)}render(FormControlWithCustomInput)
<Box display="grid" sx={{gap: 3}}><CheckboxGroup><CheckboxGroup.Label>Checkboxes</CheckboxGroup.Label><FormControl><Checkbox value="checkOne" /><FormControl.Label>Checkbox one</FormControl.Label></FormControl><FormControl><Checkbox value="checkTwo" /><FormControl.Label>Checkbox two</FormControl.Label></FormControl><FormControl><Checkbox value="checkThree" /><FormControl.Label>Checkbox three</FormControl.Label></FormControl></CheckboxGroup><RadioGroup><RadioGroup.Label>Radios</RadioGroup.Label><FormControl><Radio name="radioChoices" value="radioOne" /><FormControl.Label>Radio one</FormControl.Label></FormControl><FormControl><Radio name="radioChoices" value="radioTwo" /><FormControl.Label>Radio two</FormControl.Label></FormControl><FormControl><Radio name="radioChoices" value="radioThree" /><FormControl.Label>Radio three</FormControl.Label></FormControl></RadioGroup></Box>
<FormControl required><FormControl.Label>Name</FormControl.Label><TextInput /></FormControl>
<Box display="grid" gridGap={3}><FormControl disabled><FormControl.Label>Name</FormControl.Label><TextInput /></FormControl><FormControl disabled><FormControl.Label>Checkbox option</FormControl.Label><Checkbox /></FormControl></Box>
<Box display="grid" gridGap={3}><FormControl><FormControl.Label visuallyHidden>Name</FormControl.Label><TextInput /></FormControl><FormControl><FormControl.Label visuallyHidden>Checkbox option</FormControl.Label><Checkbox /></FormControl></Box>
We encourage using FormControl
alongside all standalone form components like TextInput
, as every input must have a corresponding label to be accessible to assistive technology.
FormControl
also provides an interface for showing a hint text caption and a validation message, and associating those with the input for assistive technology.
<Box display="grid" gridGap={3}><FormControl><FormControl.Label>Name</FormControl.Label><TextInput /><FormControl.Caption>Hint: your first name</FormControl.Caption></FormControl><FormControl><FormControl.Label>Option one</FormControl.Label><Checkbox /><FormControl.Caption>Hint: the first and only option</FormControl.Caption></FormControl></Box>
const ValidationExample = () => {const [value, setValue] = React.useState('mona lisa')const [validationResult, setValidationResult] = React.useState()const doesValueContainSpaces = inputValue => /\s/g.test(inputValue)const handleInputChange = e => {setValue(e.currentTarget.value)}React.useEffect(() => {if (doesValueContainSpaces(value)) {setValidationResult('noSpaces')} else if (value) {setValidationResult('validName')}}, [value])return (<FormControl><FormControl.Label>GitHub handle</FormControl.Label><TextInput block value={value} onChange={handleInputChange} />{validationResult === 'noSpaces' && (<FormControl.Validation variant="error">GitHub handles cannot contain spaces</FormControl.Validation>)}{validationResult === 'validName' && (<FormControl.Validation variant="success">Valid name</FormControl.Validation>)}<FormControl.Caption>With or without "@". For example "monalisa" or "@monalisa"</FormControl.Caption></FormControl>)}render(ValidationExample)
<><FormControl><FormControl.Label>Option one</FormControl.Label><FormControl.LeadingVisual><MarkGithubIcon /></FormControl.LeadingVisual><Checkbox /></FormControl><FormControl><FormControl.Label>Option two</FormControl.Label><FormControl.LeadingVisual><MarkGithubIcon /></FormControl.LeadingVisual><Checkbox /><FormControl.Caption>This one has a caption</FormControl.Caption></FormControl></>
Name | Type | Default | Description |
---|---|---|---|
children Required | FormControl.Label | FormControl.Caption | FormControl.Validation | Autocomplete | TextInput | TextInputWithTokens | Select | ||
disabled | boolean | false | Whether the control allows user input |
id | string | a generated string | The unique identifier for this control. Used to associate the label, validation text, and caption text |
required | boolean | false | If true, the user must specify a value for the input before the owning form can be submitted |
sx | SystemStyleObject | Style overrides to apply to the component. See also overriding styles. | |
ref | React.RefObject<HTMLDivElement> | A ref to the element rendered by this component. |
Name | Type | Default | Description |
---|---|---|---|
visuallyHidden | boolean | false | Whether the label should be visually hidden |
as | React.ElementType | 'label' | The label element can be changed to a 'legend' when it's being used to label a fieldset, or a 'span' when it's being used to label an element that is not a form input. For example: when using a FormControl to render a labeled SegementedControl, the label should be a 'span' |
sx | SystemStyleObject | Style overrides to apply to the component. See also overriding styles. |
Name | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | The content (usually just text) that is rendered to give contextual info about the field | |
sx | SystemStyleObject | Style overrides to apply to the component. See also overriding styles. |
Name | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | The content (usually just text) that is rendered to give contextual info about the validation result for the field | |
variant Required | 'error' | 'success' | 'warning' | Changes the visual style to match the validation status | |
sx | SystemStyleObject | Style overrides to apply to the component. See also overriding styles. |
Name | Type | Default | Description |
---|---|---|---|
children | React.ReactNode | The visual to render before the choice input's label | |
sx | SystemStyleObject | Style overrides to apply to the component. See also overriding styles. |