Before React 19, submitting HTML forms asynchronously required boilerplate code: managing `isPending` state with `useState`, handling `try/catch` blocks, managing response data, and resetting inputs. React 19's `useActionState` hook drastically simplifies this pattern by updating component state based on the result of a Form Action.
1. `useActionState` Hook Syntax
const [state, formAction, isPending] = useActionState(fn, initialState, permalink?);
Parameters & Return Values Explained:
- `fn`: The action function to execute when the form is submitted. Signature:
async (previousState, formData) => newState. - `initialState`: The value you want the state to be initially before any action has run.
- `state`: The current result returned by the latest action execution.
- `formAction`: A function to pass to your
<form action={formAction}>attribute. - `isPending`: A boolean flag indicating whether the async action is currently executing.
2. Complete Live Code Example: User Registration Form
Below is a production-ready component demonstrating `useActionState` with simulated network delay, error handling, and loading state:
// 1. Define Server Action / Async Function
async function updateProfile(previousState, formData) {
const username = formData.get("username");
const email = formData.get("email");
// Simulate API latency
await new Promise(res => setTimeout(res, 1500));
if (!username || !email) {
return { success: false, message: "Username and Email are required!" };
}
return { success: true, message: `Profile updated for ${username}!` };
}
// 2. Component Implementation
export default function ProfileForm() {
const [state, formAction, isPending] = useActionState(updateProfile, { success: null, message: "" });
return (
<form action={formAction} className="p-4 border rounded">
<div className="mb-3">
<label className="form-label">Username:</label>
<input type="text" name="username" className="form-control" disabled={isPending} />
</div>
<div className="mb-3">
<label className="form-label">Email:</label>
<input type="email" name="email" className="form-control" disabled={isPending} />
</div>
<button type="submit" className="btn btn-primary" disabled={isPending}>
{isPending ? "Updating Profile..." : "Save Changes"}
</button>
{state.message && (
<div className={`alert mt-3 ${state.success ? 'alert-success' : 'alert-danger'}`}>
{state.message}
</div>
)}
</form>
);
}
3. Key Benefits of `useActionState`
- Automatic Pending State: The returned
isPendingboolean handles loading UI indicators automatically without extrauseState(false)variables. - Native Form Data Access: Automatically receives standard HTML5
FormDataobjects inside action handlers. - Progressive Enhancement: Works seamless with Next.js App Router Server Actions for SSR rendering.
Master React 19 with Telugu IT Tutorials
Enroll in our full-stack React 19 & Next.js 15 course to learn modern frontend architecture with live hands-on projects. View Courses →