In React 19, when you pass a function to a form element's action prop (<form action={saveNote}>), what single argument does React call that function with, and what does React do to the form's uncontrolled inputs after the function finishes?
answer
- action accepts a function, not just a URL
- no event object reaches your function
- the one parameter carries the field values
- it is a FormData instance
- uncontrolled fields reset afterwards
basics
~20 sReact calls the function with one argument, a FormData object built from the form's named fields. There is no submit event, so nothing to preventDefault. After the function finishes, React automatically resets the form's uncontrolled inputs.
solid answer
~50 sIn React 19 the `action` prop accepts a function as well as a URL string. When it is a function, React handles the submission itself: it skips the browser navigation, builds a `FormData` from the fields that have a `name` attribute, and calls your function with that `FormData` as its only argument. You read values with `formData.get('title')`, or `formData.getAll('tags')` for repeated names. Because you never get an event object, calling `preventDefault()` makes no sense here — if you genuinely need the event, use `onSubmit` instead. The function may be async and React treats the submission as pending until it settles. Once it completes, React resets the form, so uncontrolled inputs go back to their `defaultValue`; controlled inputs are unaffected because their value comes from state. A `<button>` inside the form can override the target with its own `formAction` prop.
code
jsx · 21 linesfunction NewNote({ onSaved }) {
async function saveNote(formData) {
const title = formData.get('title');
const tags = formData.getAll('tag');
await fetch('/api/notes', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title, tags }),
});
onSaved();
}
return (
<form action={saveNote}>
<input name="title" />
<input type="checkbox" name="tag" value="work" />
<input type="checkbox" name="tag" value="urgent" />
<button type="submit">Save</button>
</form>
);
}go deeper
Know that <form action={fn}> calls fn with a FormData object and nothing else, and that you read fields with formData.get('name'). Say plainly that there is no event to preventDefault.
Explain that React suppresses the browser navigation itself, awaits an async action, and resets the form afterwards so uncontrolled inputs return to their defaultValue. Mention that only named controls appear in the FormData.
Show judgment about failure paths: a thrown error reaches the error boundary and the reset still discards typed input, so expected validation failures should be returned as data and echoed back into defaultValue rather than thrown.
Be ready to argue the tradeoff of standardising on form actions versus a form library: you gain a platform-shaped contract and React-managed pending windows, but give up fine-grained control over the submit event and inherit React's reset semantics team-wide.
## Two meanings of the action prop In plain HTML, a form's `action` attribute is a URL: the browser serializes the fields and navigates to it. React 19 keeps that behaviour when you pass a string, and adds a second meaning — pass a **function** and React takes the submission over. It suppresses the default navigation, collects the form's fields into a `FormData` object, and calls your function with it. ```jsx function NewNote() { async function saveNote(formData) { const title = formData.get('title'); await api.createNote({ title }); } return ( <form action={saveNote}> <input name="title" defaultValue="" /> <button type="submit">Save</button> </form> ); } ``` ## The argument is FormData, and only FormData Your function receives exactly one parameter. It is a `FormData` instance — the same web-platform type you would get from `new FormData(formElement)`. That has consequences worth knowing: - Only controls with a `name` attribute appear in it. An input you forgot to name is silently missing. - `formData.get(name)` returns the first value (a string, or a `File` for file inputs); `formData.getAll(name)` returns every value for a repeated name such as a checkbox group. - `Object.fromEntries(formData)` gives you a plain object, but it collapses repeated names to the last value, so it is not a safe default for multi-value fields. - Unchecked checkboxes are absent rather than `false`, which is the classic surprise when you expect a boolean. ## There is no event, so there is nothing to prevent A form action is not an event handler. React has already prevented the default navigation before your function runs, so there is no `event` parameter and no `preventDefault()` to call. Code that starts `function saveNote(e) { e.preventDefault(); ... }` will throw, because `e` is a `FormData` and has no such method. `onSubmit` still exists and still receives a React synthetic event; the two prop styles are alternatives, and the idiomatic React 19 form uses `action`. ## Async functions are expected The function may be `async`. React awaits it and treats the form as submitting for the whole duration, which is what makes a pending flag possible at all rather than something you hand-maintain with a `useState` boolean. ```jsx async function saveNote(formData) { await api.createNote({ title: formData.get('title') }); } ``` If the function throws, React does not quietly swallow it — the error propagates to the nearest error boundary. For an expected failure such as a validation error, `try`/`catch` inside the action and return or store an error value instead of throwing. ## The automatic reset When the action is a function, React resets the form after the submission completes. Concretely, uncontrolled inputs return to their `defaultValue` rather than keeping what the user typed. This is convenient for the common "add an item, clear the box" flow, and it catches people out in one specific case: a failed submission also resets, so the user loses the text they typed. Controlled inputs are not touched by the reset — their displayed value is whatever state says, so you clear them by clearing state. If you need typed values to survive a failed submit with uncontrolled inputs, echo the submitted values back out of the action as state and feed them into `defaultValue` so the re-render restores them. ## Per-button actions A `<button>` or `<input type="submit">` inside the form accepts its own `formAction` prop, which overrides the form's `action` for that button. One form can therefore have "Save draft" and "Publish" buttons pointing at different functions, without branching on which button was clicked inside a single handler. ```jsx <form action={publish}> <input name="title" /> <button type="submit">Publish</button> <button type="submit" formAction={saveDraft}>Save draft</button> </form> ``` ## What this buys you The payoff is not just syntax. Because React owns the submission window — it knows when the function started and when it settled — React can expose that window to the rest of the tree. That is the foundation the action hooks build on: a pending flag and a returned state come from React observing the action, instead of from a `isSubmitting` boolean you set true at the top of a handler and hope to set false in every `finally` branch.
- An input in the form has no name attribute. What does formData.get() return for it?Nothing — `FormData` is built from named form controls, so an unnamed input never appears and `get()` returns `null` for it. This is the most common reason a field "disappears" between the JSX and the action. Add a `name`, and remember that unchecked checkboxes are also absent entirely rather than present as `false`.
- A user submits, the action throws a validation error, and the typed text vanishes. What is going on?React resets the form after a function action completes, including when it fails, so uncontrolled inputs revert to their `defaultValue`. Don't throw for expected validation failures — catch inside the action and return an error result along with the submitted values, then feed those values back through `defaultValue` so the re-rendered form keeps what the user typed.
- Can you still use onSubmit in React 19, and when would you?Yes — `onSubmit` still receives a synthetic submit event and still needs `preventDefault()`. Reach for it when you actually need the event or the form element, for example to inspect `event.submitter` or to integrate with a form library that owns submission. For ordinary submits, `action` is shorter and unlocks the pending and state plumbing React provides around actions.
saying these in an interview costs you the question
- Says the action function receives the submit event
- Calls preventDefault() inside a form action function
- Thinks the browser navigates when action is a function
- Expects controlled inputs to be cleared by the reset
- Reads fields off event.target instead of the FormData