Menu

React Forms: Inputs, Checkboxes, Select and Validation

Build forms in React with controlled inputs: text fields, textarea, select, checkboxes and radio buttons, one state object for many fields, submitting with onSubmit, showing validation errors, and React 19 form actions.

This page includes runnable editors - edit, run, and see output instantly.

A React form keeps each field's value in state: the input shows value={state} and updates it with onChange. When the user submits, an onSubmit handler calls e.preventDefault() to stop the page reload and then uses the values already in state.

Type a name and press Enter or click the button. Then delete the e.preventDefault(); line: the preview shows the fields the browser would have sent in a native submission, which in a real page would also reload it.

An input whose value comes from state is called a controlled input. The alternative, letting the DOM keep the value and reading it later, is covered in controlled vs uncontrolled components.

Text inputs and textarea

Every text field works the same way: value from state, onChange reads e.target.value. A <textarea> follows the same pattern. In HTML its text goes between the tags, but in React you pass it as value.

Because you set the value yourself, you can change what the user typed before it shows: the title field turns every letter into a capital. The character counter updates on every keystroke because body is state and each change renders again.

Select

A <select> takes value on the select element itself, not selected on an option. e.target.value is the value of the chosen option.

For a multiple select, add multiple and pass an array as value. Read the choices with Array.from(e.target.selectedOptions, (o) => o.value).

Checkbox and radio buttons

A checkbox is controlled with checked, not value, and its handler reads e.target.checked, which is true or false. Radio buttons in a group share a name; each one is checked when the state equals its value.

Reading e.target.value on a checkbox is a common mistake: for a checkbox with no value attribute it returns the string "on" whether the box is ticked or not.

One state object for many fields

A form with ten fields does not need ten useState calls and ten handlers. Keep one object, give every input a name that matches a key, and write one handler that uses the name as a computed key: [e.target.name].

The handler checks the input type so the checkbox stores a boolean and the others store text. Add a field by adding a key to the starting object and an input with the same name; the handler needs no change. The spread keeps every other field (see updating arrays and objects).

Submitting a form

Put the submit logic on the form's onSubmit, not on the button's onClick. onSubmit also fires when the user presses Enter in a text field, which a click handler would miss. A <button> inside a form is a submit button by default; give other buttons type="button" so they do not submit.

Open the console, type a password and press Enter: the form logs the whole object. Clicking "Clear" logs nothing, because it is type="button". Remove that attribute and Clear starts submitting too.

Validation and showing errors

Simple validation is a function that takes the values and returns an object of messages. Run it on submit, store the result in state, and show each message under its field. Disable the submit button while a request is running so a double click does not send the form twice.

Click Save with the fields empty to see both messages, then fill them in. saveProfile is a stand-in for a real request: it waits 800 milliseconds and resolves. The noValidate attribute turns off the browser's own validation bubbles so only your messages show; drop it and add required to an input to compare.

Errors can also be computed during render instead of stored, for example const emailError = form.email && !form.email.includes('@'), which shows the message as the user types. Pick whichever timing suits the form: on submit is calmer, while typing gives faster feedback.

Form actions in React 19

React 19 adds another way to handle a form. Pass a function to the form's action prop. React prevents the page reload for you and calls the function with a FormData object holding every field that has a name. The fields do not need state at all: they are uncontrolled, and React clears them after the action finishes.

Type your own comment and post it. The list shows your text, and the input goes back to Nice docs: after the action resolves, React resets uncontrolled fields to their defaultValue. The action can be async, and while it runs React treats the update as a transition.

To show a pending state or an error from the action, wrap it with useActionState, which returns the latest result, a wrapped action for the form, and an isPending flag. A button deep inside the form can also read the pending state with useFormStatus from react-dom:

import { useFormStatus } from 'react-dom';

function SubmitButton() {
    const { pending } = useFormStatus();
    return <button disabled={pending}>{pending ? 'Posting...' : 'Post'}</button>;
}

useFormStatus is not loaded in the editors here, so it appears only in this static block. It must be called from a component rendered inside the <form>, not in the component that renders the form.

Controlled state with onSubmit and form actions with FormData both work in React 19. Controlled fields suit forms that react while you type (live validation, a character counter, fields that depend on each other). Actions suit forms where you only need the values when the user submits.

Frequently Asked Questions

How do you get the value of an input in React?

Store it in state and update it from onChange: <input value={name} onChange={e => setName(e.target.value)} />. The current text is then always in name. For checkboxes read e.target.checked instead of e.target.value.

How do you stop a form from reloading the page in React?

Call e.preventDefault() at the start of the onSubmit handler. Without it the browser performs a normal form submission, which navigates and reloads the page.

How do you handle many inputs with one state in React?

Keep one object in state, give every input a name that matches a key, and use one handler with a computed key: setForm({ ...form, [e.target.name]: e.target.value }).

Why does a textarea use value instead of children in React?

In HTML a textarea's text sits between its tags, but in React you pass it as value (or defaultValue), the same way as an input. That way every text field in a React form works the same.

What are form actions in React 19?

In React 19 you can pass a function to <form action={fn}>. React prevents the page reload, calls the function with the form's FormData, and resets uncontrolled fields when it finishes. useActionState adds a pending flag and a result on top of it.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED