Command Palette

Search for a command to run...

Hectal
PHASE 19Advanced React 19+ ~23 min· topic 3 of 8

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 name and value are 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 value and onChange.
Form reset
Putting every uncontrolled field back to its default value, like form.reset().
requestFormReset
A react-dom function 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.

Signup.jsxwhole filejsx
// 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.

What React does on submitdiagram
Rendering diagram…

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.

Editor.jsxwhole filejsx
<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.

Chat.jsxwhole filejsx
import { requestFormReset } from "react-dom";

startTransition(async () => {
  requestFormReset(form);
  await send(text);
});

Try it yourself

  1. 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 to Guest: reset means "back to the default", not "empty".)

  2. 2

    Turn a field controlled

    Make the note input controlled with useState. Submit. Predict which box empties. Then clear the note yourself by calling setNote("") after the await.

  3. 3

    Read the whole form at once

    In the editor example, log Object.fromEntries(formData) inside publish. Which keys appear when Publish is clicked, and when Save draft is clicked?

Code & diagrams

A guestbook with a form action React 19+ New tab

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

Press Run to see the page.

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
formAction: two buttons, two actions React 19+ New tab

`Save draft` uses its own function; `Publish` uses the form's. The clicked button's `name="intent" value="now"` arrives in the FormData.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

draft saved: Hooks
published: Actions (now)
requestFormReset with your own onSubmit React 19+ New tab

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

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

empty, not sent
while sending, box: [Hello]
after, box: [] list: Hello

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

terminal
$ Console
── what you'll see ──
requestFormReset was called outside a transition or action. To fix, move to an action, or wrap with startTransition.

Break #2

Forget the name attribute

In the guestbook, delete name="note" from the second input and sign.

terminal
$ Console
── what you'll see ──
action got Asha: null

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 as form.submit().

  • ▸

    Function actions submit with the get method as far as useFormStatus reports, but nothing is sent over the network unless your function does it. With Server Functions, frameworks render a real action URL 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, and useActionState (Topic 19.4) wraps an action to keep its last result.

Remember this

  1. 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 /html course on forms). In Topic 5.4 you replaced that with onSubmit plus e.preventDefault(). React 19 adds a third option: give action a function. React then handles the submit event itself: it prevents the page load, builds a **FormData** (the browser's object holding every named field) from the form and calls fn(formData) inside a transition, so it is an Action (Topic 19.2). A string URL still works exactly like HTML.

  2. 2

    Read fields with formData.get("name") (one value or null), formData.getAll("tags") for repeated names such as checkboxes, or Object.fromEntries(formData) for a plain object. Only fields with a name attribute are included. Unchecked checkboxes are missing. The submit button that was clicked is included too when it has a name and value, so <button name="intent" value="publish"> lets one action know which button was pressed.

  3. 3

    **formAction on 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 HTML formaction attribute. Use it for "Save" vs "Save draft", or "Approve" vs "Reject", without one action branching on a field.

  4. 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 with defaultValue/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. 5

    **requestFormReset(form)** from react-dom asks React to reset a form when the current Action finishes. It's for Actions you start yourself, for example from your own onSubmit handler with startTransition, where React wouldn't reset automatically. It must be called inside a transition or Action; outside one React warns requestFormReset was called outside a transition or action.

  6. 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, and Enter in a field submits. Submitting from code must use form.requestSubmit() (fires validation and the submit event); the old form.submit() skips the event, and React's placeholder action attribute then throws an error telling you to use requestSubmit. In Phase 20 you'll pass Server Functions to the same action prop, which also lets a form work before JavaScript loads.

Explain it without notes

01

What happens when a form with action={fn} is submitted?

02

When does React reset a form after an action, and which fields are affected?

03

What is formAction for, and how does an action know which button was pressed?

04

When do you need requestFormReset?

Practice

01

Build a newsletter form with an email field (type="email", required) whose action waits 300 ms and shows Subscribed: <email>.

02

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 defaultValue from 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(), not submit().