Skip to Content
React in a Week
Day 3 · Interactivity

Forms and controlled inputs

Build forms whose inputs are driven by React state, handle submits, and pass new data up to a parent.

Forms are where users give your app data: a search term, a comment, a new book for their reading list. You already have every piece you need: events to notice typing, state to remember it, and immutable updates to add the result to a list. This lesson puts them together.

Controlled inputs

A controlled input is an input whose value comes from React state. You give it two props:

  • value, which says what the input shows, read from state;
  • onChange, which runs on every keystroke and updates that state.
Live code· A controlled input

Data goes in a loop: you type, onChange fires, the setter stores the new text, React re-renders, and the input displays the value from state. Because state is the single source of truth, everything else on the page can use it right away: the greeting, the character count, and the Clear button that resets the input by setting state.

Other kinds of inputs

Most inputs work the same way. Two details are different:

  • A checkbox uses checked instead of value, and you read e.target.checked (a boolean).
  • A <select> and a <textarea> use value and onChange exactly like a text input. In React, you don't put selected on an <option> or text between the <textarea> tags.
Live code· Checkbox, select and textarea

Submitting a form

Wrap your inputs in a <form> and handle its onSubmit event. This gives you useful behavior for free: pressing Enter in a text input submits the form, and screen readers understand the structure.

Remember from the events lesson that a browser form reloads the page when submitted. Call e.preventDefault() first to stay in your React app.

Live code· Handling a submit

Three habits from that example are worth copying:

  1. e.preventDefault() is the first line of the submit handler.
  2. Validate before you save. Trimming whitespace and refusing empty text keeps junk out of your state. The disabled button gives the user a hint too.
  3. Clear the form after saving by setting the input's state back to ''.

(Using the index as a key is acceptable here only because messages are never reordered or deleted. For real data, use a stable ID, as you'll do in a moment.)

Lifting the data up: a form component

In a real app, the form and the list usually live in different components. The form owns its own input state, because nobody else needs half-typed text. The list of items lives in the parent. When the form is submitted, it calls a callback prop like onAdd to hand the finished data up.

This is the exact shape of the AddBookForm you'll build in today's checkpoint.

Live code· A form that reports to its parent

crypto.randomUUID() is built into modern browsers. It returns a random unique string like "3b241101-e2bb-4255-8caf-4136c566a962", which makes a perfect stable key.

Notice that canSubmit is not state. It's calculated from title and author on every render, so it can never get out of sync with them.

Deep diveWhat about form Actions?

React 19 added another way to handle forms: you can pass a function to <form action={...}> and React gives it the form's data. Combined with Hooks like useActionState and useOptimistic, this is excellent for forms that talk to a server.

You'll meet those on Day 6. Controlled inputs remain the foundation, and they're the right tool whenever the UI needs to react to every keystroke, as with live validation, character counts or search-as-you-type.

Try it

Sign-up form with validation

Build a sign-up form with an email input and an "I agree to the terms" checkbox. The Sign up button should stay disabled until the email contains an @ and the box is checked. On submit, show "Welcome, (email)!" in place of the form.

Live code· Challenge: sign-up form
Show solution
Live code· Solution: sign-up form

Three pieces of state hold what the user did. isValid is derived from two of them, so it's always correct. The submit handler checks isValid again, because a disabled button is a hint for people, not a guarantee in code. Showing a different screen after submit is plain conditional rendering from Day 2.

Quick check

What makes an input controlled?

Quick check

You click Submit and the whole page reloads, wiping your list. What's missing?

Quick check

How do you read the state of a checkbox in its onChange handler?

Day 3 progress

0/6

On this page