Topic 19.3
Form Actions
In one line
Pass a function to <form action={fn}> (or formAction={fn} on a button) and React calls it with the form's FormData as an Action: no preventDefault, a pending state through the form hooks, and uncontrolled fields reset automatically when it succeeds; requestFormReset resets a form on demand.
Think of it like this
A post box. You don't walk the letter to the house yourself; you drop it in the box and the postal service takes it from there. Some post offices have two slots, "Local" and "Everywhere else": same letter, different handling depending on which slot you use. A form action is the post box: React picks up the filled-in form and hands it to your function. **formAction** on a button is the second slot: the same form goes to a different function.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Form action
- A function passed to
<form action={fn}>. React calls it with the form data when the form is submitted. - FormData
- A browser object holding every named field of a form. Read it with
.get(name). - formAction
- A prop on a submit button that sends the form to a different function than the form's own action.
- Submitter
- The button that submitted the form. Its
nameandvalueare added to the FormData. - Uncontrolled field
- An input that keeps its own value in the DOM, given a starting value with
defaultValue. - Controlled field
- An input whose value comes from React state through
valueandonChange. - Form reset
- Putting every uncontrolled field back to its default value, like
form.reset(). - requestFormReset
- A
react-domfunction that resets a form when the current Action finishes.
Step by step
01From onSubmit to action
Here is the same signup in both styles. The action version has no event object, no preventDefault and no manual FormData: React does those steps.
// Topic 5.4 style
<form onSubmit={(e) => {
e.preventDefault();
const data = new FormData(e.currentTarget);
signup(data.get("email"));
}}>
// React 19 form action
<form action={async (formData) => {
await signup(formData.get("email"));
}}>02What React does on submit
The browser fires submit (after native validation passes). React prevents the page load, reads the FormData, starts a transition and calls your function. When it finishes, React applies its state updates and resets the form in one commit.
03Two buttons, two actions
A button's formAction wins over the form's action. Both receive the same FormData. Buttons without formAction use the form's action.
<form action={publish}>
<input name="title" />
<button>Publish</button>
<button formAction={saveDraft}>Save draft</button>
</form>04Which fields reset
Uncontrolled fields go back to their defaultValue or defaultChecked. Controlled fields keep the value in your state. If you want a controlled field cleared, set its state to "" in the action after the request succeeds.
05Reset on demand with requestFormReset
When you keep your own onSubmit (for example to validate first) and start the Action yourself, call requestFormReset(form) inside it. The reset waits until the Action ends, like the automatic one.
import { requestFormReset } from "react-dom";
startTransition(async () => {
requestFormReset(form);
await send(text);
});Try it yourself
- 1
Make a field survive the reset
In the guestbook, give the name input
defaultValue="Guest". Predict what it shows after signing as Asha. (It goes back toGuest: reset means "back to the default", not "empty".) - 2
Turn a field controlled
Make the note input controlled with
useState. Submit. Predict which box empties. Then clear the note yourself by callingsetNote("")after theawait. - 3
Read the whole form at once
In the editor example, log
Object.fromEntries(formData)insidepublish. Which keys appear when Publish is clicked, and when Save draft is clicked?
Code & diagrams
No `onSubmit`, no `preventDefault`. The fields keep their text while the request runs and empty when the entry appears. Leave Name empty and press Sign: `required` stops the submit before React is involved.
Preview
Console
Nothing logged yet.Expected console output
action got Asha: Lovely course
right after submit, name box: Asha
after the action, name box: []
list: Asha: Lovely course`Save draft` uses its own function; `Publish` uses the form's. The clicked button's `name="intent" value="now"` arrives in the FormData.
Preview
Console
Nothing logged yet.Expected console output
draft saved: Hooks
published: Actions (now)Validation runs first in our own handler. The reset is requested at the start of the Action but happens when it finishes, so the text stays visible while sending.
Preview
Console
Nothing logged yet.Expected console output
empty, not sent
while sending, box: [Hello]
after, box: [] list: HelloBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Call requestFormReset outside an Action
In the chat example move requestFormReset(form) out of startTransition, directly into handleSubmit.
Break #2
Forget the name attribute
In the guestbook, delete name="note" from the second input and sign.
Myth vs fact
Myth
Form actions only work with Server Functions.
Fact
Any function works. A client function runs in the browser; a Server Function (Phase 20) makes the same form call the server.
Myth
React clears every field after an action.
Fact
Only uncontrolled fields, only after success, and only back to their default values. Controlled fields follow your state.
Myth
With action I can't use onSubmit any more.
Fact
You can still use onSubmit with preventDefault and start the Action yourself. You then reset with requestFormReset.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
React renders a placeholder
action="javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. ...')"on forms with function actions. It only runs if something bypasses React's submit handling, such asform.submit(). - ▸
Function actions submit with the
getmethod as far asuseFormStatusreports, but nothing is sent over the network unless your function does it. With Server Functions, frameworks render a realactionURL so the form posts even before hydration (progressive enhancement, Phase 20). - ▸
Under the hood a form action uses the same machinery as
useActionState: the reset is applied in the same commit as the Action's final updates, so the user never sees the empty form before the result. - ▸
A form action makes the whole form a transition.
useFormStatus(Topic 19.5) reads its pending state from inside the form, anduseActionState(Topic 19.4) wraps an action to keep its last result.
Remember this
- 1
In plain HTML,
<form action="/signup">names a URL: submitting makes the browser send the fields there and load a new page (see the/htmlcourse on forms). In Topic 5.4 you replaced that withonSubmitpluse.preventDefault(). React 19 adds a third option: giveactiona function. React then handles thesubmitevent itself: it prevents the page load, builds a **FormData** (the browser's object holding every named field) from the form and callsfn(formData)inside a transition, so it is an Action (Topic 19.2). A string URL still works exactly like HTML. - 2
Read fields with
formData.get("name")(one value ornull),formData.getAll("tags")for repeated names such as checkboxes, orObject.fromEntries(formData)for a plain object. Only fields with anameattribute are included. Unchecked checkboxes are missing. The submit button that was clicked is included too when it has anameandvalue, so<button name="intent" value="publish">lets one action know which button was pressed. - 3
**
formActionon a button** (or<input type="submit">) overrides the form's action for that button only:<button formAction={saveDraft}>Save draft</button>. It's the React version of the HTMLformactionattribute. Use it for "Save" vs "Save draft", or "Approve" vs "Reject", without one action branching on a field. - 4
Automatic reset. When a function action finishes successfully, React resets the form, like the browser's
form.reset(): every uncontrolled field (one that keeps its own value, set withdefaultValue/defaultChecked, Topic 5.2) returns to its default. Controlled fields (value+onChange) are not touched, because their value comes from your state; clear that state yourself. The reset happens together with the Action's last render, so the field keeps its text while the request runs and empties when the result appears. If the Action throws, there is no reset, so the user doesn't lose their typing. - 5
**
requestFormReset(form)** fromreact-domasks React to reset a form when the current Action finishes. It's for Actions you start yourself, for example from your ownonSubmithandler withstartTransition, where React wouldn't reset automatically. It must be called inside a transition or Action; outside one React warnsrequestFormReset was called outside a transition or action. - 6
Form actions keep working with everything you know about forms:
required,type="email"and other native validation run before the action (an invalid form is never submitted), labels and accessibility work as in Phase 18, andEnterin a field submits. Submitting from code must useform.requestSubmit()(fires validation and the submit event); the oldform.submit()skips the event, and React's placeholderactionattribute then throws an error telling you to userequestSubmit. In Phase 20 you'll pass Server Functions to the sameactionprop, which also lets a form work before JavaScript loads.
Explain it without notes
What happens when a form with action={fn} is submitted?
When does React reset a form after an action, and which fields are affected?
What is formAction for, and how does an action know which button was pressed?
When do you need requestFormReset?
Practice
Build a newsletter form with an email field (type="email", required) whose action waits 300 ms and shows Subscribed: <email>.
Add an Approve and a Reject button to a form; both submit the same comment field but call different functions.
Trade-offs
- ↔
Form actions cut boilerplate and pair with Server Functions, but they favour uncontrolled fields. Live validation as you type still needs controlled state or a form library (Topic 5.7).
- ↔
Automatic reset is right for "send and start over" forms (comments, chat) and wrong for edit forms where the saved values should stay; use controlled fields or set
defaultValuefrom the saved data there.
Done when you can
I can pass a function to
<form action>and read the FormData.I can send one form to different functions with
formAction.I know which fields reset after an action, and when.
I can reset a form from my own Action with
requestFormReset.I submit forms from code with
requestSubmit(), notsubmit().