useFormStatus is a React DOM Hook that reports the submission status of a parent form. A submit button rendered inside the form can read pending to disable itself, relabel while the request runs, and show the data being sent. The Hook must live in a child component, not in the component that renders the form element.
It solves a specific problem: a button deep in the form tree cannot easily know when the parent form is submitting, and useFormStatus gives it that signal without prop drilling.
Read the pending state
Call useFormStatus inside a button component, then read the pending flag to disable the button and swap its label during submission.
import { useFormStatus } from "react-dom";
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? "Submitting..." : "Submit"}
</button>
);
}The button now disables and changes its label the moment the form starts submitting. Disabling it also prevents a second click from sending the same request twice.
Put the button inside the form
The button component must be rendered inside a form element for useFormStatus to see anything. Wrap it in a form that has an action function.
export default function SignupForm() {
async function submit(formData) {
await fetch("/api/signup", { method: "POST", body: formData });
}
return (
<form action={submit}>
<label htmlFor="email">Email</label>
<input id="email" name="email" type="email" />
<SubmitButton />
</form>
);
}Submit the form and the SubmitButton inside it switches to its pending label. The Hook reads the form that wraps it, so the button stays in sync with the request without any props being passed down.
Read the submitted data
The status object also carries data, the FormData the form is submitting. A component can use it to show what the user just sent.
function SubmitButton() {
const { pending, data } = useFormStatus();
return (
<>
<button type="submit" disabled={pending}>Submit</button>
{data && <p>Sending {data.get("email")}</p>}
</>
);
}While the request runs, data holds the submitted fields, so the paragraph shows the email being sent. The same pattern can confirm a username choice or echo a summary of the form.
Read method and action
Beyond pending and data, the status object exposes method and action. method is the form's HTTP method as a string, get or post, and action is a reference to the function passed to the form's action prop. Most submit buttons only need pending, but these extras help when one shared button renders inside several forms and must behave differently per form.
Keep in mind that action is null when the form has no action prop or when it uses a URL, so do not rely on it being a function.
Avoid the same-component pitfall
useFormStatus only tracks a parent form, not a form rendered in the same component that calls the Hook. Calling it in the component that returns the form element always gives pending false.
- Extract the button into its own component and render it inside the form.
- Pass values to the button as props when it needs more than the status.
- Keep the action function in the parent so the child stays presentational.
This is the most common mistake with useFormStatus, and the fix is always the same: move the Hook call one level down into a child of the form.
Combine with useActionState
useFormStatus reports that a form is submitting, while useActionState adds result state and its own pending flag. You can use both: keep the state in the parent with useActionState, and read pending in a child button with useFormStatus. The button then stays presentational, and the parent owns the result.
This split is common in larger forms where the submit button and the error display live far apart in the tree.
Why a separate button component
The extraction is not just a workaround. A separate SubmitButton can be reused across every form in an app, so pending behavior stays consistent. It also keeps the parent form focused on data and validation instead of button labels.
For the full submission flow with result state, see React 19 Form Actions and useActionState explained. For the error side, see how to show server validation errors in React forms, and for the basics see how to build forms in React.
Rune AI
Key Insights
- Import useFormStatus from react-dom.
- Call it in a component rendered inside the form.
- Read pending to disable the submit button.
- Read data to show what is being submitted.
- It tracks only the parent form, not sibling forms.
Frequently Asked Questions
Where do I import useFormStatus from?
Why is my pending value always false?
What does useFormStatus return?
Conclusion
useFormStatus reads the parent form's submission status so a child button can disable and relabel itself. Keep the Hook inside the form, and pair it with a form action.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.