Skip to content

Other Form Controls

In addition to the text inputs we've been using so far, the web platform provides lots of additional form controls. They include:

  • Textareas
  • Radio buttons
  • Checkboxes
  • Selects
  • Ranges
  • Color pickers

If you've ever had to work with these form controls outside of React, you've likely been surprised/frustrated by how dissimilar they are from one another.

For example, textareas define the current value as children, rather than using a value prop:

<textarea>
This is the current value
</textarea>

As another example, select tags use a selected prop on one of the <option> children to signify the selected value:

<select>
<option value="a">
Option 1
</option>
<option value="b" selected>
Option 2
</option>
<option value="c">
Option 3
</option>
</select>

Here's the good news: React has tweaked many of these form controls so that they all behave much more similarly. There's a lot less chaos with form controls in React.

Essentially, all form controls follow the same pattern:

  1. The current value is locked using either value (for most inputs) or checked (for checkboxes and radio buttons).
  2. We respond to changes with the onChange event listener.

In this lesson, we're going to go over a couple of common examples. You'll also find a bonus cheatsheet at the end of this module that provides a quick reference for all form controls!

Select tag

The <select> tag allows the user to select a single option from a list of predefined options.

When working with select tags in React, they work pretty much exactly like text inputs. We use value and onChange. Here's an example:

Code Playground

import React from 'react';

function App() {
const [
selectedOption,
setSelectedOption
] = React.useState('red');

return (
<form>
<fieldset>
<legend>
What is your favourite color?
</legend>
<select
value={selectedOption}
onChange={event => {
setSelectedOption(event.target.value)
}}
>
<option value="red">
Red
</option>
<option value="green">
Green
</option>
<option value="blue">
Blue
</option>
</select>
</fieldset>
<p>
Selected value:
<br />
{selectedOption}
</p>
</form>
);
}

export default App;

By setting the value prop, we make this a controlled component, binding the selected option to our React state. When we add the onChange event listener, we allow this state to be changed by selecting a different option from the list.

Honestly, this feels like a huge improvement to me, compared to the default functionality of this form control. We don't have to fuss with adding the selected attribute to one of the <option> children; instead, we use the same pattern we know and love.

Radio buttons

Alright, so radio buttons are a bit trickier.

Ostensibly, they serve the same purpose as a <select> tag; they allow the user to select 1 choice from a group of options.

The tricky thing, though, is that this state is split across multiple independent HTML elements. There's only one <select> tag, but there are multiple <input type="radio"> buttons!

Let's start by looking at an example of a controlled radio button group in React:

Code Playground

import React from 'react';

function App() {
const [
value,
setValue
] = React.useState('no');

return (
<form>
<fieldset>
<legend>Do you agree?</legend>
<input
type="radio"
name="agreed-to-terms"
id="agreed-yes"
value="yes"
checked={value === "yes"}
onChange={event => {
setValue(event.target.value)
}}
/>
{' '}
<label htmlFor="agreed-yes">
Yes
</label>
<br />
<input
type="radio"
name="agreed-to-terms"
id="agreed-no"
value="no"
checked={value === "no"}
onChange={event => {
setValue(event.target.value)
}}
/>
{' '}
<label htmlFor="agreed-no">
No
</label>
</fieldset>
</form>
);
}

export default App;

Phew, that's a lot of properties! Let's look at each one in turn.

  • name — The browser needs to know that each button is part of the same group, so that ticking one option will untick the others. This is done through the name prop. Each radio button in a group should share the same name.
  • value — Each radio button has its own value. This property will be copied over to our React state when the option is ticked. This is the definition / meaning for each radio button.
  • id — like other form controls, this is needed so that the <label> can be associated with the right input, so that clicking the label focuses the input.
  • checked — This is the prop that binds a given radio button to our React state, making it a controlled value. It should be set to a boolean value: true if it's ticked, false if it's not. Only one radio button should be set to true at a time.

Even though it looks quite different, it does follow the same rough formula. We use an onChange event listener to detect when a given option is ticked. When that happens, we update the React state.

For most other inputs, we bind React state to the value prop. In this case, though, we don't have a single value prop to bind to, since we have multiple radio buttons. So instead, we bind to checked, controlling the ticked/unticked status for each button in the group with React state.

There are lots of other form controls, but React does a good job making sure that they all follow the same conventions. For a complete reference, check out the bonus cheatsheet at the end of the module