<form action> & useFormStatus: Form Lifecycle and Pending State
- Last updated
- Prerequisites:
- HTML Form submission
- React 19 Actions
- react
- forms
- useFormStatus
- form-data
- actions
- react-dom
- react-19
Read at your depth
The practical view
React 19 extends standard HTML forms by accepting async functions in `<form action={fn}>`, `<button formAction={fn}>`, and `<input formAction={fn}>`. React automatically extracts and passes the native `FormData` object to the action function. The `useFormStatus` hook lets child components read the parent form's submission state (`pending`, `data`, `method`, `action`) without prop drilling.
The same idea in other frameworks
`<form action={asyncAction}>` with `useFormStatus()` in child submit buttons.Reactive Forms `FormGroup` with `form.statusChanges` observables.`@submit.prevent` with custom `isSubmitting` reactive refs.SvelteKit `<form method="POST" use:enhance>` with `$page.form`.Legacy vs modern
Manual e.preventDefault() + Local Loading State vs Native React 19 Form Action
Manual submit handlers require boilerplate state and prop drilling; React 19 handles FormData extraction and status tracking natively.
<form onSubmit={handleSubmit}><SubmitButton loading={loading} /></form> // ❌ Manual wiring<form action={saveAction}><SubmitButton /></form> // ✅ NEW: Zero state boilerplateInterview gotchas
Context
Interviewers test your understanding of Context boundaries in React DOM.
The mechanical answer
`useFormStatus` **must be called from a component rendered inside the `<form>`**, not in the component that renders the `<form>` itself. It behaves like a React Context consumer: calling it in the parent component rendering the `<form>` will return `pending: false` because it cannot read its own child form's status context.
Trap
Placing `const { pending } = useFormStatus()` in the same component that returns `<form>...</form>` will not work; extract the submit button into a separate child component.
Context
Checks knowledge of error propagation during form action dispatch.
The mechanical answer
If a `<form action>` function throws an error, React catches the exception and propagates it to the nearest **Error Boundary**. If you want to capture form return values or validation errors as local component state without throwing, use `useActionState` instead of a plain `<form action>`.
Trap
Do not expect `<form action={fn}>` to set a return value onto form state automatically; pair it with `useActionState` when response data is needed.