React 18 introduced new hooks aimed at simplifying form handling in React applications, especially those leveraging server components, progressive enhancement, or async data flows. Among these are two similarly named but functionally different hooks: useActionState
and useFormState
.
At first glance, these hooks may appear to solve the same problem—handling form submissions—but they are designed for different use cases. Understanding the difference between them is essential to building clean, modern, and efficient forms in React or frameworks like Next.js.
In this blog, we’ll explore:
- What
useActionState
anduseFormState
are - How each hook works
- Key differences between them
- When to use one over the other
- Practical examples to help you decide which is right for your project
What Is useActionState
?
useActionState
is a React hook that manages local state based on a user-triggered action—most commonly a form submission. It’s designed for client components and allows you to define an asynchronous function that processes submitted form data and returns a new state object.
Key Features:
- Accepts a reducer-like function
(prevState, formData) => nextState
- Allows asynchronous logic inside the reducer function
- Returns an action function tied to the
<form action={...}>
attribute - Suitable for local, client-side feedback (like success or error messages)
Basic Example:
import { useActionState } from 'react';
function ContactForm() {
const [formState, formAction] = useActionState(async (prev, formData) => {
const name = formData.get('name');
if (!name) return { error: 'Name is required' };
return { success: `Hello, ${name}!` };
}, { error: null, success: null });
return (
<form action={formAction}>
<input type="text" name="name" placeholder="Enter your name" />
<button type="submit">Submit</button>
{formState.error && <p style={{ color: 'red' }}>{formState.error}</p>}
{formState.success && <p style={{ color: 'green' }}>{formState.success}</p>}
</form>
);
}
This hook is ideal for building forms that process client-side logic like validations, form data transformations, or async client-side actions (like sending a message via API).
What Is useFormState
?
useFormState
is a server-aware hook commonly used in frameworks like Next.js App Router. It’s designed for server actions and works within React’s server components. Rather than defining logic in the component, the processing happens in a separate server action, and the hook ties the result of that action to the client UI.
Key Features:
- Integrates with server actions via
app/
directory in Next.js - Accepts a server action and an initial state
- Automatically re-renders the component with updated state from the server
- Supports seamless form submission across client and server boundaries
Basic Example (Next.js App Router):
'use client';
import { useFormState } from 'react';
async function createUser(prevState: any, formData: FormData) {
const name = formData.get('name') as string;
if (!name) return { error: 'Name is required' };
return { success: `Welcome, ${name}` };
}
export default function ServerForm() {
const [formState, formAction] = useFormState(createUser, { error: null, success: null });
return (
<form action={formAction}>
<input type="text" name="name" />
<button type="submit">Submit</button>
{formState.error && <p>{formState.error}</p>}
{formState.success && <p>{formState.success}</p>}
</form>
);
}
This setup allows the form to be processed on the server while keeping UI updates reactive and fast.
Key Differences Between useActionState
and useFormState
Feature | useActionState | useFormState |
---|---|---|
Where It Runs | Client components only | Server actions (used in client/server mix) |
Use Case | Local form handling and client logic | Forms processed by server functions |
Async Support | Yes, async functions supported | Yes, works with server async functions |
Framework Dependency | Pure React (optional in any React app) | Requires React Server Components / Next.js |
Return Value | Local state from client reducer function | Server-side state returned to the client |
Suitable For | Error messages, loading states, validation | Server mutations, DB writes, secure actions |
Input Format | (prevState, formData) => nextState | Same format, but runs server-side |
When to Use useActionState
You should use useActionState
when:
- You are working in a React client component
- You want to handle form submission and feedback entirely on the client
- You don’t need to interact with a database or secure APIs directly from the server
- You want to avoid excessive boilerplate with
useState
oruseReducer
- You are managing UI-only feedback like showing a “Thank you” message
Examples:
- Client-side form validation
- Submitting feedback via third-party APIs
- Login form that hits a public API
- Dynamic forms in dashboards and admin panels
When to Use useFormState
You should use useFormState
when:
- You’re building with Next.js App Router and React Server Components
- You want the form to trigger a server-side mutation, like updating a database
- You want the result of that mutation passed back to the client automatically
- You need to securely handle logic that should not run on the client
- You want seamless integration with server actions and server routing
Examples:
- Creating or updating users in a database
- Submitting a support ticket via server-side logic
- Admin form for changing product details
- Forms that interact with protected server APIs
Performance and Security Implications
One important distinction is around security. Since useFormState
processes the form server-side, it keeps sensitive logic (e.g., authentication, data access) off the client, which is more secure.
On the other hand, useActionState
runs fully on the client. This makes it fast and responsive but not ideal for sensitive operations that require authentication or interaction with protected data sources.
In terms of performance, useActionState
gives you immediate feedback without a round trip to the server. However, useFormState
can be optimized on the server using streaming and caching, especially in production deployments using edge runtimes like Vercel.
Can You Use Both in One Project?
Yes—absolutely. In fact, many modern projects use both. Here’s how:
- Use
useFormState
for server-sensitive actions (e.g., database, authentication). - Use
useActionState
for client-only feedback or UI interaction (e.g., popups, alerts, async local behavior).
This hybrid approach helps you balance performance and security based on context.
Conclusion
React 18 brought new capabilities for form handling through hooks like useActionState
and useFormState
. While both hooks allow you to simplify and structure your form logic, they serve different purposes and are optimized for different environments.
- Use
useActionState
for local, client-side form interactions. - Use
useFormState
for server-driven logic where secure, persistent actions are required.
Understanding when and how to use each hook will make your forms cleaner, safer, and more aligned with modern React architecture—especially in server-aware environments like Next.js.
By choosing the right hook for the job, you’ll write less boilerplate, create more predictable forms, and deliver a better user experience.