SubmitButton pattern, child component constraint, localized form feedback.
When a form is submitting, users need feedback exactly where they initiated the action. A global loading bar doesn't tell them which button is working. useFormStatus provides the pending state of the nearest parent form—enabling localized, contextual feedback.
The hook returns { pending, data, method, action }, but pending is what you'll use most. It becomes true when the form submits and false when the action completes.
The most common use is a reusable submit button that shows its own loading state:
'use client';
import { useFormStatus } from 'react-dom';
export function SubmitButton({ children }) {
const { pending } = useFormStatus();
return (
<Button type="submit" disabled={pending}>
{pending ? 'Saving...' : children}
</Button>
);
}Drop this into any form—no prop drilling, no context providers, no state management:
<form action={createPost}>
<Input name="title" />
<SubmitButton>Create Post</SubmitButton>
</form>The button automatically shows a spinner during submission and disables to prevent double-clicks.
Here's the critical detail: useFormStatus must be called from a child component of the form. It doesn't work in the same component that renders the form:
// ❌ Won't work - same component as form
function BadForm() {
const { pending } = useFormStatus(); // Always false
return <form action={action}>...
This constraint exists because React needs to track which form the status belongs to—and that relationship is established through the component tree.
Use the pattern for any form feedback:
function FormProgress() {
const { pending } = useFormStatus();
if (!pending) return null;
return <Progress value=The principle: button click → button feedback. Keep pending indicators close to where users took action.
<form action={createPost}>
<Input name="title" />
<SubmitButton>Create Post</SubmitButton>
</form>