React Form Events
Table of Contents + −
In the last lesson you learned about React Passing Arguments to Event Handlers. Now let’s point that at forms, where a plain HTML form reloads the page on submit and wipes your state, so this lesson handles onSubmit and onChange the right way.
🤔 Why do forms need special handling?
A normal HTML form has a built-in habit that fights against React.
- When you submit a form, the browser reloads the page by default. That’s old behaviour from before JavaScript apps.
- A reload throws away everything in memory. So all your React state is gone in an instant.
- You don’t want that. You want to grab what the user typed and keep it, not lose it.
So we need two things: stop that automatic reload, and read the typed value ourselves. That’s exactly what onSubmit and onChange give us.
📤 The onSubmit event
When the user submits a form, React fires an onSubmit event on the <form> tag. You put your handler right there.
This form runs handleSubmit whenever the user submits it, whether they click the button or press Enter.
function NameForm() { function handleSubmit(e) { console.log("Form was submitted!"); }
return ( <form onSubmit={handleSubmit}> <input /> <button type="submit">Submit</button> </form> );}A couple of things to notice here.
- The
onSubmitgoes on the<form>tag, not on the button. The form is the thing being submitted. - You pass the handler by name,
onSubmit={handleSubmit}, with no parentheses. React calls it for you when the event happens. - React hands your handler an event object, usually called
e. It carries info about what just happened, and it has the tools we need next.
🛑 Why you need e.preventDefault()
That event object e has one method we care about for forms.
e.preventDefault()tells the browser “don’t do your default thing”.- For a form, that default thing is reloading the page.
Let’s see both versions side by side. The wrong one lets the page reload, the right one stops it.
// ❌ Wrong - no preventDefault, the page reloads and your state is lostfunction handleSubmit(e) { console.log("submitted"); // browser reloads here... everything resets}
// ✅ Right - preventDefault stops the reload, your code keeps runningfunction handleSubmit(e) { e.preventDefault(); console.log("submitted, page stays put");}Here’s what each one does.
- Without
e.preventDefault(), the browser reloads the page the instant you submit. Your component runs from scratch, so any typed value disappears. - With
e.preventDefault(), you stop that reload. The page stays where it is, and your code runs normally so you can use the value. - Calling it first, right at the top of the handler, is the safe habit. Do it before anything else.
Forget this and your form looks broken
If you skip e.preventDefault(), the page will flash and reload every time the user submits. It feels like a bug, but it’s just the browser’s default behaviour. Always call it first in a form’s submit handler.
⌨️ Reading input with onChange
Stopping the reload is half the job. The other half is knowing what the user typed, and for that we use the onChange event on the input.
Every time the user types a character, React fires onChange. The new value sits at e.target.value, and we save it into state.
import { useState } from "react";
function NameForm() { const [name, setName] = useState("");
return ( <input value={name} onChange={(e) => setName(e.target.value)} /> );}Let’s break down that input line by line.
e.targetis the input element that fired the event. Soe.target.valueis whatever is currently typed in the box.onChange={(e) => setName(e.target.value)}runs on every keystroke and saves the new text into ournamestate.value={name}ties the box back to state, so what you see in the box is always whatevernameholds.
So now React always knows the latest value, and the input stays in sync the whole time.
💡 Putting it together: capture a name on submit
Now let’s join both events into one small working form. The user types a name, hits submit, and we show a greeting, with no page reload.
import { useState } from "react";
function NameForm() { const [name, setName] = useState(""); const [greeting, setGreeting] = useState("");
function handleSubmit(e) { e.preventDefault(); // stop the page from reloading setGreeting("Hello, " + name); }
return ( <div> <form onSubmit={handleSubmit}> <input value={name} onChange={(e) => setName(e.target.value)} /> <button type="submit">Greet me</button> </form> <p>{greeting}</p> </div> );}Here’s the full flow, step by step.
- As the user types,
onChangekeeps saving the text intoname. - On submit,
handleSubmitruns. Firste.preventDefault()stops the reload. - Then we build the greeting from
nameand save it withsetGreeting. - React re-renders, and the
<p>shows the new greeting. The typed name is still there too.
Output
(user types "Alex" and clicks Greet me)
Hello, AlexSo the whole point lands here: because we stopped the reload, the state survived, and we could use the typed value right away.
✅ Best practices
A few small habits keep your form handlers clean and bug-free.
- Call
e.preventDefault()as the very first line of your submit handler. Get it out of the way before anything else. - Put
onSubmiton the<form>, not on the button. That way Enter and the button both work. - Read the typed value from
e.target.valueinsideonChange, and keep it in state. - Always pair
value={...}withonChangeon an input, so the box and state stay in sync.
Submit fires from Enter too
Because onSubmit lives on the form, pressing Enter inside an input submits it as well, not just clicking the button. That’s a real reason to use a form instead of a lone button with onClick.
🧩 What You’ve Learned
- ✅ A plain form reloads the page on submit, which wipes your React state
- ✅
onSubmitgoes on the<form>tag and runs your handler when the form is submitted - ✅
e.preventDefault()stops the page reload, so call it first in the submit handler - ✅
onChangefires on every keystroke, and the typed text is ate.target.value - ✅ Save that value into state with a setter, and tie the input back with
value={...} - ✅ Together,
onSubmitandonChangelet you capture form data without losing state
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
Why do you call e.preventDefault() in a form's submit handler?
Why: By default a form submit reloads the page, and a reload resets your component and loses state. e.preventDefault() stops that default behaviour so your code can run and use the value.
- 2
Where should the onSubmit event go?
Why: onSubmit belongs on the <form> tag. That way it fires whether the user clicks the button or presses Enter inside an input.
- 3
Inside an onChange handler, how do you read what the user typed?
Why: e.target is the input that fired the event, so e.target.value is the current text in that box. You usually save it into state with a setter.
- 4
What happens if you leave out e.preventDefault() in a React form submit?
Why: Without e.preventDefault(), the browser does its default thing and reloads the page on submit. That resets the component, so any typed value disappears.
🚀 What’s Next?
Now you can catch a form submit, stop the reload, and read what the user typed. Next we’ll look at another kind of event, this time from the keyboard, like reacting to specific keys as the user presses them.