Conditionally revealed form content
Revealing additional form questions and help content after a user selects an option can reduce the amount of content a user must read when completing complex forms.
Do
- limit what is revealed to a single set of related questions or information that does not require its own submit action
- reveal the related set of questions under the initial input or controls
- use another page if there are multiple related questions that need a submit action
- only use with vertically stacked checkboxes or radios.
Don’t
- reveal a complete form with submit action
- use with horizontally stacked checkboxes or radios
- reveal anything other than questions or supporting information relating directly to the initial option selected by the user.
Radio
When a user selects a specific option from a Radio control group, additional questions or helpful information relevant to their selection can be revealed. Revealing content after a selection is made ensures a user will only see content that is relevant to them.
If there a large number of items, use Select instead.
() => { const [value, setValue] = React.useState(); const handlerForKey = React.useCallback((key) => () => setValue(key), []); const isChecked = (key) => key === value; return ( <FormStack> <ControlGroup label="Preferred contact method" required block> <Radio checked={isChecked('email')} onChange={handlerForKey('email')}> Email </Radio> <Radio checked={isChecked('phone')} onChange={handlerForKey('phone')}> Phone </Radio> <Radio checked={isChecked('textMessage')} onChange={handlerForKey('textMessage')} > Text message </Radio> </ControlGroup> <ConditionalFieldContainer visible={isChecked('email')}> <TextInput type="email" label="Email address" required /> </ConditionalFieldContainer> <ConditionalFieldContainer visible={isChecked('phone')}> <TextInput label="Phone number" inputMode="numeric" required /> </ConditionalFieldContainer> <ConditionalFieldContainer visible={isChecked('textMessage')}> <TextInput label="Mobile phone number" inputMode="numeric" required /> </ConditionalFieldContainer> </FormStack> ); };
Checkbox
When a user selects a particular Checkbox, you can reveal additional questions or provide helpful information that is relevant to the selection after the Checkbox control group. Revealing content after a selection is made ensures a user will only see content that is relevant to them.
() => { const [value, setValue] = React.useState([]); const handlerForKey = React.useCallback( (key) => () => setValue((value) => value.includes(key) ? value.filter((v) => v !== key) : [...value, key] ), [] ); const isChecked = (key) => value.includes(key); return ( <FormStack> <ControlGroup label="How would you like to be contacted?" required block> <Checkbox checked={isChecked('email')} onChange={handlerForKey('email')} > Email </Checkbox> <Checkbox checked={isChecked('phone')} onChange={handlerForKey('phone')} > Phone </Checkbox> <Checkbox checked={isChecked('textMessage')} onChange={handlerForKey('textMessage')} > Text message </Checkbox> </ControlGroup> <ConditionalFieldContainer visible={value.length}> {isChecked('email') && ( <TextInput type="email" label="Email address" required /> )} {isChecked('phone') && ( <TextInput inputMode="numeric" label="Phone number" required /> )} {isChecked('textMessage') && ( <TextInput inputMode="numeric" label="Mobile phone number" required /> )} </ConditionalFieldContainer> </FormStack> ); };
Select
When a user selects a particular option from a Select dropdown, additional questions or helpful information relevant to their selection can be revealed. This ensures that the user only sees content that is pertinent to their choice.
If there a small number of items, use Radio instead.
() => { const [value, setValue] = React.useState(''); const handlerForKey = React.useCallback((e) => setValue(e.target.value), []); const isChecked = (key) => value === key; return ( <FormStack> <Select placeholder="Please select" label="How would you like to be contacted?" onChange={handlerForKey} options={[ { value: 'post', label: 'Post' }, { value: 'textMessage', label: 'Text message' }, { value: 'email', label: 'E mail' }, ]} required value={value} /> <ConditionalFieldContainer visible={isChecked('post')}> <H2>Address</H2> <TextInput inputMode="text" label="Street address" maxWidth="xl" required type="text" /> <TextInput inputMode="text" label="Suburb, town or city" maxWidth="lg" required type="text" /> <Select label="State or territory" maxWidth="sm" options={[ { label: 'ACT', value: 'act' }, { label: 'NSW', value: 'nsw' }, { label: 'NT', value: 'nt' }, { label: 'QLD', value: 'qld' }, { label: 'SA', value: 'sa' }, { label: 'TAS', value: 'tas' }, { label: 'VIC', value: 'vic' }, { label: 'WA', value: 'wa' }, ]} required /> <TextInput inputMode="numeric" label="Post code" required maxWidth="sm" /> </ConditionalFieldContainer> <ConditionalFieldContainer visible={isChecked('textMessage')}> <TextInput inputMode="text" label="Mobile phone number" required /> </ConditionalFieldContainer> <ConditionalFieldContainer visible={isChecked('email')}> <TextInput type="email" label="Email address" required /> </ConditionalFieldContainer> </FormStack> ); };
Invalid
When a conditionally revealed question is invalid, include an error message on the invalid field that is clearly related to the initial question.
() => { const [value, setValue] = React.useState('email'); const handlerForKey = React.useCallback((key) => () => setValue(key), []); const isChecked = (key) => key === value; return ( <FormStack> <ControlGroup label="Preferred contact method" required block> <Radio checked={isChecked('email')} onChange={handlerForKey('email')}> Email </Radio> <Radio checked={isChecked('phone')} onChange={handlerForKey('phone')}> Phone </Radio> <Radio checked={isChecked('textMessage')} onChange={handlerForKey('textMessage')} > Text message </Radio> </ControlGroup> <ConditionalFieldContainer visible={isChecked('email')}> <TextInput type="email" label="Email address" required invalid message="Enter a email address" /> </ConditionalFieldContainer> <ConditionalFieldContainer visible={isChecked('phone')}> <TextInput label="Phone number" inputMode="numeric" required invalid message="Enter a phone number" /> </ConditionalFieldContainer> <ConditionalFieldContainer visible={isChecked('textMessage')}> <TextInput label="Mobile phone number" inputMode="numeric" required invalid message="Enter a mobile phone number" /> </ConditionalFieldContainer> </FormStack> ); };
Research
March 2025
Before March 2025, conditional form elements were revealed immediately after their parent radio or checkbox, and inside their control group.
This pattern worked well for sighted users who could see the relationship and context between the control and conditionally revealed form elements. However, we observed screen reader users becoming disoriented when they encountered additional form elements between items in the control group. In response, we adjusted our guidance to position the additional questions immediately after the control group.
This updated pattern has been tested in two rounds of usability studies. We have observed no issues since the change.
Related components
- Conditional field container – A standardised pattern for conditionally hiding and revealing the amount of content a user views in a form.