The useFormStatus Hook
By Flavio Copes
Learn how the React useFormStatus hook reads a form action's pending state from a nested component like a shared submit button, without prop drilling.
useFormStatus lets a component read the status of the form that contains it, most commonly to know if a submission is pending. No props needed: the hook finds the parent form on its own, like a context consumer.
Similarly to how we can get an action’s pending state through useActionState, we can use useFormStatus to get a form action’s pending state from a component that’s included in a form.
Why would you want this? Think of a submit button component. It’s common to be its own component, shared across different forms across the app. Passing a pending prop into it from every form gets old fast. With useFormStatus, the button reads the state itself:
"use client"
import { useActionState } from "react"
import { useFormStatus } from 'react-dom'
import { myServerAction } from './actions'
const initialState = {
message: "",
}
export const Demo = () => {
const [state, formAction, pending] =
useActionState(myServerAction, initialState)
return (
<div>
<form action={formAction}>
<input
type='text'
name='fullName'
/>
{state?.message && <p>{state.message}</p>}
<SubmitButton />
</form>
</div>
)
}
const SubmitButton = () => {
const { pending } = useFormStatus()
return (
<button
aria-disabled={pending}
type='submit'>
{pending ? "Submitting..." : "Submit"}
</button>
)
}
While the action runs, pending is true and the button shows “Submitting…”. When the action finishes, it flips back to false.
What the hook returns
useFormStatus takes no arguments and returns an object with four fields:
pending:truewhile the parent form is submittingdata: aFormDataobject with the values being submitted, ornullmethod:'get'or'post'action: a reference to the function passed to the form’sactionprop
Most of the time you only need pending. The data field is useful when you want to show what’s being sent, like an optimistic username preview.
Two details to remember. The hook lives in react-dom, not react, so watch your import. And since it’s a hook, the component using it must be a client component, hence the "use client" directive in the example.
Why is pending always false?
Here’s the mistake everyone makes once. useFormStatus only reports the status of a <form> above the component calling it.
If you call it in the same component that renders the form, there is no parent form to read from, and pending stays false forever. That’s exactly why SubmitButton is a separate component in the example above: it’s rendered inside the form, so the hook works.
If your button shows no pending state, check where the hook is called. Move it into a child component of the form and it starts working.