React.js + Next.js · Module 3. Forms and Effects · Lesson 2 · Point 2
React Form Actions and defaultValue
In Point 1, we submitted a form with onSubmit.
Now we will use a shorter method called a Form Action. React will collect the form values and give them directly to our function.
We will also use defaultValue to put starting text inside an input.
We will learn one small step at a time with the Pizza Palace order form.
REACT · 01
1. What Is a Form Action?
A Form Action is a function that runs when a form is submitted.
Think of action as answering this question:
What should React do with this form after the customer submits it?
For Pizza Palace, the action could read the customer's name, read the chosen pizza, and prepare the order.
The Traditional HTML Meaning
In normal HTML, action usually contains a URL:
<form action="/orders" method="post">
This tells the browser to send the form data to /orders.
The React 19 Meaning
In React 19, action can receive a function instead of a URL:
<form action={handleOrder}>
This tells React:
When this form is submitted, run the
handleOrderfunction.
We pass the function without parentheses. React will run it later, after the customer submits the form.
function handleOrder(formData: FormData) {
// Read and process the order here.
}
React collects the form's named fields and gives them to handleOrder inside a FormData object.
Pizza Palace Mental Model
The <form> = the customer's order sheet
The name fields = labels such as customerName and pizzaName
FormData = all completed answers collected together
handleOrder = the job that processes the order
action = the instruction to run that job
The action is not the form data. The action is the function that receives and processes the form data.
What Happens After Submit?
1. The customer completes the form
2. The customer clicks the submit button
3. React collects every field that has a name
4. React creates FormData
5. React calls handleOrder(formData)
6. Our function processes the order
Why Is It Shorter Than onSubmit?
With onSubmit, our function first receives an event:
function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const form = event.currentTarget;
const formData = new FormData(form);
}
With a Form Action, our function receives FormData immediately:
function handleSubmit(formData: FormData) {
// The submitted values are ready to read.
}
React handles these repeated steps for us:
event.preventDefault();
const form = event.currentTarget;
const formData = new FormData(form);
The Main Difference
onSubmit |
Form Action |
|---|---|
| An event handler | A form-processing function |
| Receives a submit event | Receives FormData |
Creates FormData manually |
Gets FormData from React |
Usually calls preventDefault() |
React handles the submission |
Remember
onSubmit={handleSubmit}means: “Run this event handler when submission happens.”action={handleOrder}means: “Use this function to process the submitted form.”- A React Form Action receives
FormData, not a submit event.
REACT · 02
2. Make Your First Form Action
File: src/App.tsx
export default function App() {
function handleSubmit(formData: FormData) {
console.log("Form submitted");
}
return (
<form action={handleSubmit}>
<label htmlFor="customerName">Customer name</label>
<input id="customerName" name="customerName" />
<button type="submit">Continue</button>
</form>
);
}
Browser Result
action.What Happens?
- We give
handleSubmitto the form. - The customer clicks Continue.
- React runs
handleSubmit. - React gives the function the submitted form data.
FormData is a built-in browser API. We do not import it from React.
This first example does not read the customer's name yet. It only shows that the function runs.
REACT · 03
3. See What React Does for You
Imagine that the customer types Nikita and clicks Continue:
Customer types "Nikita"
↓
Customer submits the form
↓
React collects every named field
↓
React creates FormData
↓
React calls handleSubmit(formData)
↓
Our function processes the values
Compare the function signatures:
// Point 1: receives an event
function handleSubmit(event: FormEvent<HTMLFormElement>) {}
// Point 2: receives FormData directly
function handleSubmit(formData: FormData) {}
The first function gets an event. The second function gets the form values directly.
These are short syntax examples, not complete pages, so they do not need browser previews.
REACT · 04
4. Read One Form Value
Use .get() with the input's name:
export default function App() {
function handleSubmit(formData: FormData) {
const customerName = formData.get("customerName");
if (typeof customerName === "string") {
console.log("Customer:", customerName);
}
}
return (
<form action={handleSubmit}>
<label htmlFor="customerName">Customer name</label>
<input id="customerName" name="customerName" />
<button type="submit">Continue</button>
</form>
);
}
Browser Result
get("customerName") matches name="customerName".Why Do We Check the Type?
The return type of .get() is:
FormDataEntryValue | null
In simple words, the result may be:
- text;
- a file;
- nothing, if the field does not exist.
Some lessons use a type assertion:
const customerName = formData.get("customerName") as string;
as string tells TypeScript, “I expect text.” It does not check the real value. The typeof check in our complete example is safer.
Remember the Match
name="customerName" ↔ get("customerName")
A control without a name is not included in FormData.
REACT · 05
5. Start an Input with defaultValue
Use defaultValue when an input should already contain text when the page opens:
export default function App() {
function handleSubmit(formData: FormData) {
const pizzaName = formData.get("pizzaName");
console.log("Pizza:", pizzaName);
}
return (
<form action={handleSubmit}>
<label htmlFor="pizzaName">Pizza</label>
<input
id="pizzaName"
name="pizzaName"
defaultValue="Margherita"
/>
<button type="submit">Choose pizza</button>
</form>
);
}
Browser Result
Margherita when the page first opens.defaultValue sets only the starting value. The customer can edit it. If they replace Margherita with Pepperoni, FormData receives Pepperoni.
This is called an uncontrolled input. It simply means the browser remembers what the customer types. We do not need React state for every change.
REACT · 06
6. Build a Simple Pizza Order
Now combine a customer name with a prefilled pizza choice.
File: src/App.tsx
export default function App() {
function handleOrder(formData: FormData) {
const customerName = formData.get("customerName");
const pizzaName = formData.get("pizzaName");
if (
typeof customerName !== "string" ||
typeof pizzaName !== "string"
) {
return;
}
console.log("Customer:", customerName);
console.log("Pizza:", pizzaName);
}
return (
<form action={handleOrder}>
<label htmlFor="customerName">Customer name</label>
<input id="customerName" name="customerName" required />
<label htmlFor="pizzaName">Pizza</label>
<input
id="pizzaName"
name="pizzaName"
defaultValue="Margherita"
/>
<button type="submit">Add to order</button>
</form>
);
}
Browser Result
Pepperoni because the customer changed the initial value.Follow the Example
- The page opens with
Margheritain the pizza field. - The customer changes it to
Pepperoni. - React supplies the current values to
handleOrder. - The type checks confirm that both values are text.
- The function logs the order.
REACT · 07
7. Choose defaultValue or value
These two props may look similar, but they have different jobs:
| Prop | Job | Who manages the current input? |
|---|---|---|
defaultValue |
Sets the initial value | The browser |
value |
Sets the current value | React state |
onChange |
Updates state as the user types | Your React function |
Uncontrolled Input
<input name="pizzaName" defaultValue="Margherita" />
This needs no state. It is a good choice when you only need the value after the customer submits the form.
Controlled Input
import { useState } from "react";
import type { ChangeEvent } from "react";
export default function App() {
const [pizzaName, setPizzaName] = useState("Margherita");
function handlePizzaChange(event: ChangeEvent<HTMLInputElement>) {
const newPizzaName = event.currentTarget.value;
setPizzaName(newPizzaName);
}
return (
<div className="order-editor">
<label htmlFor="pizzaName">Pizza</label>
<input
id="pizzaName"
name="pizzaName"
value={pizzaName}
onChange={handlePizzaChange}
/>
<p>Current choice: {pizzaName}</p>
</div>
);
}
Browser Result
handlePizzaChange is a normal named function. React runs it whenever the customer changes the input.
Use defaultValue for our simple order form. Use value with state when the page must react every time the customer types. We will study controlled inputs later.
REACT · 08
8. See What Happens After Submit
After the Form Action finishes successfully, React resets uncontrolled inputs automatically.
An input that starts empty becomes empty again:
<input name="customerName" />
An input with a default returns to that default:
<input name="pizzaName" defaultValue="Margherita" />
Browser Result
Controlled inputs behave differently because React state owns their values. Change the state when you want to reset them.
REACT · 09
9. Know When to Use Each Method
Both methods are correct. Choose the one that makes your form easier to build.
| Question | onSubmit |
Form Action |
|---|---|---|
| Form prop | onSubmit={handleSubmit} |
action={handleSubmit} |
| Handler receives | React form event | FormData |
preventDefault() |
Usually written manually | Handled by React |
Create FormData |
Manually | React provides it |
| Uncontrolled reset | Usually call form.reset() |
Automatic after success |
Use onSubmit when you need the submit event or want to perform every step yourself.
Use a Form Action when you want React to give you FormData directly. It needs less code for our simple Pizza Palace form.
For this lesson, choose the Form Action. It is shorter and easier to read.
REACT · 10
10. Common Mistakes and Final Example
Common Mistakes
1. Expecting an event
// Incorrect with action={handleSubmit}
function handleSubmit(event: FormEvent<HTMLFormElement>) {}
The action function receives FormData, not a submit event.
2. Forgetting the name
// This value will not appear in FormData.
<input defaultValue="Margherita" />
3. Calling the function immediately
Incorrect: action={handleSubmit()}
Correct: action={handleSubmit}
4. Treating as string as validation
A type assertion only informs TypeScript. It does not inspect the runtime value.
5. Confusing defaultValue with value
defaultValue sets an uncontrolled input's initial value. value sets a controlled input's current value.
Final Point 2 Example
export default function App() {
function handleOrder(formData: FormData) {
const customerName = formData.get("customerName");
const pizzaName = formData.get("pizzaName");
if (
typeof customerName !== "string" ||
typeof pizzaName !== "string"
) {
return;
}
console.log(`${customerName} ordered ${pizzaName}`);
}
return (
<form action={handleOrder}>
<label htmlFor="customerName">Customer name</label>
<input id="customerName" name="customerName" required />
<label htmlFor="pizzaName">Pizza</label>
<input
id="pizzaName"
name="pizzaName"
defaultValue="Margherita"
/>
<button type="submit">Place order</button>
</form>
);
}
Browser Result
Point 2 Quick Reference
| Code | Meaning |
|---|---|
action={handleOrder} |
Give the action function to React |
function handleOrder(formData: FormData) |
Receive submitted data directly |
formData.get("pizzaName") |
Read a named field |
typeof pizzaName === "string" |
Confirm that the value is text |
defaultValue="Margherita" |
Set an uncontrolled input's initial value |
Practice
- Add an email field with a matching
nameand.get()key. - Prefill the pizza field with
Pepperoni. - Change the prefilled value before submitting and inspect the result.
- Explain why the customer-name field becomes empty after success.
- Explain why the pizza field returns to its default after success.
Further Reading
- React: Handle form submission with a Form Action
- React: Providing an initial value for an input
- MDN: FormData
Next point: Organizing form fields into reusable React components.