CodingNeed.

2 · Build one skill at a time · 18 MIN

Make an input and a form work together

A controlled input displays the value held in state.

The input value comes from name, and onChange copies each edit back into name using the event’s target value. This closes the loop between state and what the user sees. Submitting a browser form normally navigates; preventDefault keeps this small app on the same page. Store a submitted message separately so it changes only when the user submits. A visible label names the input for everyone, including screen-reader users.

Type one letter and follow onChange into setName. Submit and observe that message is separate from name. Trim for validation; do not silently erase the input. Add a length branch before the success message.

Before you start

Install Node.js 22.12+ (or a supported newer LTS). In a terminal run: npm create vite@latest codingneed-react -- --template react. Then cd codingneed-react, npm install, and npm run dev. Open the local URL printed by Vite. Replace src/App.jsx with ONE lesson example at a time. JSX renders a browser interface; it is not a plain console script.

File for this example: App.jsx

New words, explained

controlled
React state is the source of an input’s displayed value.
handler
A function invoked when an event occurs.
validation
Checking whether input meets an explicit rule.

Follow the example step by step

  1. Type one letter and follow onChange into setName.
  2. Submit and observe that message is separate from name.
  3. Trim for validation; do not silently erase the input.
  4. Add a length branch before the success message.

You are ready to move on when: Enter submits the form without reloading. Whitespace-only names are rejected. A 41-character name is rejected and remains editable.

Read the example

import {useState} from 'react';
export default function App() {
  const [name, setName] = useState('');
  const [message, setMessage] = useState('');
  function submit(event) {
    event.preventDefault();
    setMessage(name.trim() ? 'Welcome, ' + name.trim() : 'Please enter a name.');
  }
  return <main><h1>Join a study group</h1><form onSubmit={submit}>
    <label htmlFor="name">Your name</label>
    <input id="name" value={name} onChange={event => setName(event.target.value)} />
    <button type="submit">Join</button>
  </form><p role="status">{message}</p></main>;
}
Check the expected output
Typing changes the input. Submitting Ada shows Welcome, Ada. Submitting spaces shows Please enter a name.

Your challenge

Reject names longer than 40 characters with a helpful message. Keep the user’s typed text available for correction.

Solution cost: Discuss the operations in this small example; rendering and I/O costs depend on the host. time · Proportional to the example’s retained data. space

Common trap

A value prop without an onChange handler makes this controlled input read-only.

Study the project implementation
import {useState} from 'react';
export default function App() {
  const [name, setName] = useState('');
  const [message, setMessage] = useState('');
  function submit(event) {
    event.preventDefault();
    setMessage(name.trim() ? 'Welcome, ' + name.trim() : 'Please enter a name.');
  }
  return <main><h1>Join a study group</h1><form onSubmit={submit}>
    <label htmlFor="name">Your name</label>
    <input id="name" value={name} onChange={event => setName(event.target.value)} />
    <button type="submit">Join</button>
  </form><p role="status">{message}</p></main>;
}

Further reading: Official documentation

Next lesson: Render a list and an empty state

Essential cookies keep your account signed in. Optional analytics is not configured on this site. Your choice does not affect access to lessons.

Read the Privacy Policy. You can change this choice in the footer.