React Form Events

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 onSubmit goes 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 lost
function handleSubmit(e) {
console.log("submitted");
// browser reloads here... everything resets
}
// ✅ Right - preventDefault stops the reload, your code keeps running
function 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.target is the input element that fired the event. So e.target.value is whatever is currently typed in the box.
  • onChange={(e) => setName(e.target.value)} runs on every keystroke and saves the new text into our name state.
  • value={name} ties the box back to state, so what you see in the box is always whatever name holds.

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, onChange keeps saving the text into name.
  • On submit, handleSubmit runs. First e.preventDefault() stops the reload.
  • Then we build the greeting from name and save it with setGreeting.
  • 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, Alex

So 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 onSubmit on the <form>, not on the button. That way Enter and the button both work.
  • Read the typed value from e.target.value inside onChange, and keep it in state.
  • Always pair value={...} with onChange on 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
  • onSubmit goes 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
  • onChange fires on every keystroke, and the typed text is at e.target.value
  • ✅ Save that value into state with a setter, and tie the input back with value={...}
  • ✅ Together, onSubmit and onChange let you capture form data without losing state

Check Your Knowledge

Test what you learned. Pick an answer for each question, then click Check.

  1. 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. 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. 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. 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.

React Keyboard Events

Share & Connect