Using state with form controls in React
After learning the fundamentals of React state, it’s time to apply them in real-world scenarios. Practicing with useState to handle UI interactions is a great next step. In this article, we’ll see how to synchronize state with various HTML form controls.
Input text
Take a look at the following example. The state variable name is defaulted to Steave. The onChange prop propagates the input's value to the setName setter, which updates the state.
import { useState } from "react";
function App() {
const [name, setName] = useState("Steve");
return (
<div>
<input
type="text"
value={name}
onChange={e => setName(e.target.value)}
/>
<p>Hello {name}</p>
</div>
);
}
e.target.value is the way you access the actual data. The e represents the synthetic event object that React creates. Once you access .target it becomes a native DOM element (HTMLElement in most of the cases), so you can use .value to get the actual value.
Radio button
Radio buttons map to a single value. You already know how the prop onChange works with e.target.value triggering a new render when the user changes the selection. To be able to show the user selection, we have used gender === "male" and gender === "female", so the option is mutually exclusive.
function App() {
const [gender, setGender] = useState("male");
return (
<div>
<label>
<input
type="radio"
value="male"
checked={gender === "male"}
onChange={e => setGender(e.target.value)}
/>
Male
</label>
<label>
<input
type="radio"
value="female"
checked={gender === "female"}
onChange={e => setGender(e.target.value)}
/>
Female
</label>
<p>Selected: {gender}</p>
</div>
);
} Here's how the above example looks in CodePen.
Textarea
Textarea works similar to an input text.
function App() {
const [content, setContent] = useState("Hello\nWorld");
return (
<div>
<textarea
value={content}
onChange={e => setContent(e.target.value)}
/>
<pre>{content}</pre>
</div>
);
}
Notice the value of content is shown inside a <pre> tag so it can render the new lines.
Select
For select inputs the onChange prop must be used with a select tag.
function App() {
const [country, setCountry] = useState("CA");
return (
<>
<select value={country} onChange={e => setCountry(e.target.value)}>
<option value="CA">Canada</option>
<option value="US">United States</option>
<option value="CO">Colombia</option>
</select>
<p>Selection: {country}</p>
</>
);
} Let's check how this works in CodePen.
Single Checkbox
For a single checkbox, state handling is similar to a text input. One caveat is that in plain HTML, the default value of a checkbox is "on". In React, you should use the checked prop to control whether the checkbox is selected or not.
import React, { useState } from "https://esm.sh/react@19";
import { createRoot } from "https://esm.sh/react-dom@19/client";
function App() {
const [notificationsEnabled, setNotificationsEnabled] = useState(true)
return (
<>
<label>
<input
type="checkbox"
checked={notificationsEnabled}
onChange={(e) => {
setNotificationsEnabled(e.target.checked)
}}
/>
I want to receive notifications
</label><br/><br/>
Notifications are: {notificationsEnabled ? "ENABLED" : "DISABLED"}
</>
)
}
const root = createRoot(document.getElementById("app"))
root.render(<App />) Let's check how this works in CodePen.
Multiple Checkboxes
There are several ways to track the state for multiple interrelated checkboxes. In the following example, I've used a simple array to store the different values of these tags in the state. Take a look at how the onChange prop uses e.targe.value to conditionally trigger changes in the state using either addFeed or removeFeed.
import React, { useState } from "https://esm.sh/react@19";
import { createRoot } from "https://esm.sh/react-dom@19/client";
function App() {
const [feeds, setFeeds] = useState(['traveling']);
function addFeed(feed) {
setFeeds([...feeds, feed])
}
function removeFeed(feed) {
setFeeds(feeds.filter(item => item !== feed))
}
return (
<>
<label>
<input
type="checkbox"
checked={feeds.includes("technology")}
onChange={(e) => {
e.target.checked ? addFeed("technology") : removeFeed("technology")
}}
/>
Technology
</label>
<br/>
<label>
<input
type="checkbox"
checked={feeds.includes("traveling")}
onChange={(e) => {
e.target.checked ? addFeed("traveling") : removeFeed("traveling")
}}
/>
Traveling
</label>
<p>Interests: {feeds.join(', ')}</p>
</>
);
}
const root = createRoot(document.getElementById("app"))
root.render(<App />) Finally, let's check how this looks in CodePen.