Display loading or managing states for form is much easier now with the new useActionState hook, whether you are using server actions or not. Let's see how to do it.
What is useActionState?
useActionState is a hook that allows you to update the state based on the form action. A Form action is a function triggered when a form is submitted. It could be a server action or a simple onSubmit function.
This hook is only available in React's Canary version. You don't need to install anything, if you are using a new nextjs app. You can install the canary version by running the following command:
npm install react@canary react-dom@canary
We will create a simple login form that will trigger a server action to log in to the user.
I have an entire tutorial about the useActionState hook where I have explained more deeply. I went over not only loading states but also how to handle input reset problems and how to update the state from the useActionState hook.
How to use useActionState with server actions?
A server action is a function that runs only on the server. So, you get access to all the server resources like database, file system, etc.
// src/actions.js
'use server'
// Simulate a server action
const wait = (ms = 3000) => new Promise(resolve => setTimeout(resolve, ms))
export const login = async (prevState, formData) => {
const email = formData.get('email')
const password = formData.get('password')
if (!email || !password) {
return {
error: 'Please fill in all fields',
message: null,
}
}
await wait()
return {
error: null,
message: 'Log in successful',
}
}
Explanation:
- Adding a
use serverdirective to the file will make all functions a server action. -
loginaction will take two arguments:prevStateandformData.-
prevStateis the return value of the previous function call if the function was called more than once. Otherwise, it is going to be the initial state, passed as the second argument touseActionState. -
formDatais the form data object that contains all the form field values.
-
- Get all the form field values using the
formData.getmethod. - If the email or password is empty, return an error message.
- Wait for 3 seconds to simulate a server request.
- Assuming a successful response and Return a success message.
Now, let's create a login form and use the useActionState hook to display loading states.
// src/LoginForm
'use client' // if using nextjs
const Page = () => {
const [state, formAction, isPending] = useActionState(login, {
message: null,
error: null,
})
const { message, error } = state
return (
<div className='flex justify-center items-center h-screen'>
<form action={formAction} className='w-96'>
<label className='form-control w-full'>
<div className='label'>
<span className='label-text'>Email</span>
</div>
<input
type='email'
placeholder='Email'
className='input input-bordered w-full '
name='email'
/>
</label>
<label className='form-control w-full'>
<div className='label'>
<span className='label-text'>Password</span>
</div>
<input
type='password'
placeholder='Password'
className='input input-bordered w-full '
name='password'
/>
</label>
<div className='label'>
<button disabled={isPending} className='btn btn-primary w-full'>
{isPending ? (
<span
className='loading loading-ring
loading-md'
></span>
) : (
Login
)}
</button>
</div>
{error && (
<div role='alert' className='alert alert-error'>
<span>Error! {error}</span>
</div>
)}
{message && (
<div role='alert' className='alert alert-success'>
<span>{message}</span>
</div>
)}
</form>
</div>
)
}
Explanation:
- The
useActionStatehook takes two arguments: the action function and the initial state object. - The hook returns three values:
state,formAction, andisPending.-
stateis the return value of theloginaction. -
formActionis the function which you have passed to theuseActionStatehook. -
isPendingis a boolean value that is true when the form is submitted.
-
- Pass the
formActionfunction to the form'sactionattribute. - Disable the button when the form is submitted and show a loading spinner.
- Show an error or success message based on the state.
That's it! You have successfully added loading states with server actions and the useActionState hook in Nextjs 15 and React 19.
If it was helpful, please drop a 🩷 and subscribe to my YouTube channel for more tutorials.
Top comments (1)
Getting the error "useActionState is not a function or its return value is not iterable" I have installed the canary version as well