
With React 19, the team continues to push the boundaries of progressive enhancement and form handling in modern web applications. One of the most exciting additions is the useActionState()
hook.
What is useActionState()
?
useActionState()
is a new hook introduced in React 19 to simplify form handling by managing the state and response of form submissions.
It works especially well in React Server Components (RSC) or when using Server Actions (like in Next.js 14+).
In short, it’s the React-native way to:
-
Track the result of a server action
-
Maintain form state
-
Handle loading, success, and error UI
The Syntax
const [state, formAction, isPending] = useActionState( actionFunction, // Function to run on submit initialState, // Initial state initializerFn? // Optional function to compute the next state );
-
state
– current state (result of the last action) -
formAction
– function to assign to the<form action={...}>
-
isPending
– boolean, true when the action is running, can be used to show a loading indicator.
When Should You Use useActionState()
?
Use it when you need:
-
Server form submissions (like
POST
requests) -
To handle async form data (login, comment, feedback)
-
Easy tracking of pending/loading/error/success UI
🔧 Example 1: Simple Contact Form with Server Action
'use client'; import { useActionState } from 'react'; async function sendMessage(_, formData) { const name = formData.get('name'); const message = formData.get('message'); await new Promise(r => setTimeout(r, 1000)); // simulate network return { success: true, name }; } export default function ContactForm() { const [result, formAction, isPending] = useActionState(sendMessage, { success: false }); return ( <form action={formAction}> <input name="name" placeholder="Your name" required /> <textarea name="message" placeholder="Your message" required /> <button type="submit" disabled={isPending}> {isPending ? 'Sending...' : 'Send'} </button> {result.success && <p>Thank you, {result.name}!</p>} </form> ); }
This example:
-
Uses
useActionState()
to submit a form -
Handles
isPending
for a loading state -
Renders a success message after submission
Example 2: Handling Validation Errors
You can return custom error messages from the action:
async function login(_, formData) { const username = formData.get('username'); const password = formData.get('password'); if (username !== 'admin' || password !== '1234') { return { error: 'Invalid credentials' }; } return { success: true }; }
In the component:
Works Great with React Server Actions
In Next.js 14+ (App Router), you can define the action in a server file and pass it to useActionState
.
Tips
-
The action function can be a server action if using frameworks like Next.js
-
useActionState
automatically handlesFormData
, so your action receives it as the second parameter -
Works well with
useFormStatus()
(also new in React 19) for per-button loading indicators.
Final Thoughts
React 19’s useActionState()
brings powerful form capabilities that bridge the gap between client interactivity and server logic, without the overhead of full-blown state management.
It’s perfect for modern React apps that want to simplify form submission and handle async actions more elegantly. However for complex forms with lot of validations, you may need a third-party package.
Â