patternjavascriptreactTip
useFormStatus reads the form's pending state in any child component
Viewed 0 times
React 19 / react-dom
useFormStatusreact-domform pendingserver actionsreact 19submit buttonform status
browsernodejs
Problem
With React 19 Server Actions, a form's pending state lives in the form element itself. To disable a submit button or show a spinner while the form submits, you previously had to thread isPending down as a prop from useTransition.
Solution
Use useFormStatus inside any component that is a descendant of a form element:
import { useFormStatus } from 'react-dom';
// SubmitButton reads the form's status — no prop drilling
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Saving...' : 'Save'}
</button>
);
}
// Parent form — SubmitButton automatically reads its status
function ProfileForm() {
async function updateProfile(formData) {
'use server';
await db.updateProfile({
name: formData.get('name'),
bio: formData.get('bio'),
});
}
return (
<form action={updateProfile}>
<input name="name" />
<textarea name="bio" />
<SubmitButton /> {/ no props needed /}
</form>
);
}
import { useFormStatus } from 'react-dom';
// SubmitButton reads the form's status — no prop drilling
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Saving...' : 'Save'}
</button>
);
}
// Parent form — SubmitButton automatically reads its status
function ProfileForm() {
async function updateProfile(formData) {
'use server';
await db.updateProfile({
name: formData.get('name'),
bio: formData.get('bio'),
});
}
return (
<form action={updateProfile}>
<input name="name" />
<textarea name="bio" />
<SubmitButton /> {/ no props needed /}
</form>
);
}
Why
useFormStatus uses React context internally to read the pending state of the nearest ancestor form element. This removes the need to thread pending state down through props and keeps the SubmitButton component portable across any form.
Gotchas
- useFormStatus must be called inside a component that is a child of the form — not in the same component that renders the form
- Only works with forms that have an action prop pointing to a Server Action or an async function
- Imported from 'react-dom' not 'react'
- Returns { pending, data, method, action } — data is the FormData currently being submitted
Code Snippets
Reusable submit button with useFormStatus
import { useFormStatus } from 'react-dom';
function SubmitButton({ label }) {
const { pending } = useFormStatus();
return <button disabled={pending}>{pending ? 'Loading...' : label}</button>;
}Context
When building forms with React 19 Server Actions and needing pending state in child components
Revisions (0)
No revisions yet.