Skip to main content

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

javascript
const { pending, data, method, action } = useFormStatus();
FieldDescription
pendingtrue while the form is running the Action (an analog of isSubmitting)
dataA FormData object with the submitted data
methodThe form's method (GET, POST, etc.)
actionA 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

javascript
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 inside SubmitButton, but it automatically knows that it belongs to the form where action={formAction} is specified;
  • pending becomes true on submit, and React updates the button;
  • all of this without manual useState and setLoading.

Features of useFormStatus

1. Works only inside descendants of <form>

The hook gets the form's context from the nearest parent:

javascript
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

javascript
<form action={myServerAction}> ... </form>

or

javascript
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:

javascript
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()

HookWhere it is usedWhat it provides
useActionState()In the component where the Action is declaredManages the Action's state (errors, result, pending)
useFormStatus()Inside descendants of <form>Provides the status of the form itself (pending, data, method)
Typical roleSubmit logic and data updatesDisplaying 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

javascript
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)

javascript
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 isLoading state or useState;
  • 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 ready
Premium

A concise answer to help you respond confidently on this topic during an interview.