What is useFormStatus?
What useFormStatus() is
useFormStatus()is a React hook that provides the current state of a form tied to an Action. It reports whether the submit is running (pending), what data was submitted (data), whether there is an error (error), and so on.
Put simply:
useFormStatus() gives you access to the state of the form itself,
similar to how useActionState() manages the state of an Action.
Syntax
const { pending, data, method, action } = useFormStatus();| Field | Description |
|---|---|
pending | true while the form is running the Action (an analog of isSubmitting) |
data | A FormData object with the submitted data |
method | The form's method (GET, POST, etc.) |
action | A reference to the Action function attached to the form |
How it works
React 19 now understands <form> as a managed element:
- On submit, React automatically calls the Action (server or client);
- While the Action is running, React updates
useFormStatus()inside that form; - Components nested in the form can read this state and show, for example, a spinner or an error message.
Example: a form loading indicator
import { useActionState, useFormStatus } from 'react';
async function sendMessage(prev, formData) {
const message = formData.get('message');
await api.send(message);
return { success: true };
}
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Sending...' : 'Send'}
</button>
);
}
export default function ChatForm() {
const [state, formAction] = useActionState(sendMessage, null);
return (
<form action={formAction}>
<input name="message" />
<SubmitButton />
{state?.success && <p>Message sent!</p>}
</form>
);
}Here:
useFormStatus()runs insideSubmitButton, but it automatically knows that it belongs to the form whereaction={formAction}is specified;pendingbecomestrueon submit, and React updates the button;- all of this without manual
useStateandsetLoading.
Features of useFormStatus
1. Works only inside descendants of <form>
The hook gets the form's context from the nearest parent:
function MyButton() {
const { pending } = useFormStatus(); // works only if inside a <form>
...
}If called outside a <form>, it returns an "empty" state (pending = false, etc.).
2. Works with Server Actions and Client Actions
<form action={myServerAction}>
...
</form>or
const [state, formAction] = useActionState(myClientAction, initial);
<form action={formAction}>...</form>In both cases useFormStatus() tracks the execution status of that Action.
3. Lets you build reusable button and indicator components
For example:
function SubmitButton({ children }) {
const { pending } = useFormStatus();
return (
<button disabled={pending}>
{pending ? 'Loading...' : children}
</button>
);
}Now this button can be used in every form without extra logic.
4. Supports optimistic UI patterns
You can show temporary states (for example, clearing fields, adding a "message sent" note) while the Action is running.
5. Works with any form method (GET, POST, etc.)
React passes method and data into useFormStatus(),
so you can use them in the UI when needed.
Difference from useActionState()
| Hook | Where it is used | What it provides |
|---|---|---|
useActionState() | In the component where the Action is declared | Manages the Action's state (errors, result, pending) |
useFormStatus() | Inside descendants of <form> | Provides the status of the form itself (pending, data, method) |
| Typical role | Submit logic and data updates | Displaying state (buttons, indicators, fields) |
They often work as a pair:
useActionState() manages the action,
and useFormStatus() manages the form's visual state.
A combined example
async function updateEmail(prev, formData) {
const email = formData.get('email');
await api.updateEmail(email);
return { success: true };
}
function EmailForm() {
const [state, formAction] = useActionState(updateEmail, null);
const { pending } = useFormStatus();
return (
<form action={formAction}>
<input name="email" />
<button disabled={pending}>
{pending ? 'Saving...' : 'Save'}
</button>
{state?.success && <p>Email updated!</p>}
</form>
);
}A more advanced example (separate components)
function FormField({ label, name }) {
const { pending } = useFormStatus();
return (
<div className={pending ? 'disabled' : ''}>
<label>{label}</label>
<input name={name} disabled={pending} />
</div>
);
}
function SaveButton() {
const { pending } = useFormStatus();
return (
<button disabled={pending}>
{pending ? 'Saving...' : 'Save'}
</button>
);
}
function SettingsForm() {
const [state, formAction] = useActionState(saveSettings, null);
return (
<form action={formAction}>
<FormField label="Username" name="username" />
<FormField label="Email" name="email" />
<SaveButton />
{state?.success && <p>Saved!</p>}
</form>
);
}All the fields and the button automatically know that the form is running, without props, context, or isLoading.
Summary
useFormStatus()is a new React 19 hook that lets any component inside a<form>know that the form is running an asynchronous action, and react to it.
Key advantages:
- No manual
isLoadingstate oruseState; - Works with both client and server Actions;
- Simplifies writing buttons, spinners, disabled logic;
- Lets you build universal, reusable forms;
- Deeply integrated with
useActionState().
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.