DEV Community

Joodi
Joodi

Posted on

3 2 2 2 1

Mastering useActionState in React

Image description

If you're diving into modern React — especially with React Server Components — there's one powerful but underrated hook you should know about: useActionState.

This hook is available in React’s experimental and Canary builds, and it brings a smarter way to manage form-related state directly tied to form submissions.

In this article, we’ll break down what useActionState does, why it’s useful, and walk through real-life examples so you can confidently integrate it into your apps.


🔍 What is useActionState?

useActionState is a React hook designed to handle state updates that occur as a result of a form submission.

Think of it as a more declarative way to handle the result of an action — like submitting data to a server or validating a field.

Syntax:

const [state, formAction] = useActionState(fn, initialState, permalink?);
Enter fullscreen mode Exit fullscreen mode
  • fn: A function triggered on form submission. It receives the previous state and form data.
  • initialState: The default value before any submission happens.
  • permalink (optional): A persistent URL used for state preservation (great for server components).

It returns:

  • state: The current form state.
  • formAction: A handler you pass into the <form> as the action prop.

🧪 Example 1: Simple Counter Form

Let’s say we want a button that increments a counter each time it’s clicked (submitted). Here’s how we can do it:

import { useActionState } from 'react';

async function increment(prevState, formData) {
  return prevState + 1;
}

function CounterForm() {
  const [count, formAction] = useActionState(increment, 0);

  return (
    <form action={formAction}>
      <p>Counter: {count}</p>
      <button type="submit">Increment</button>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

Every time the form is submitted, the increment function runs and updates the counter.


❗ Example 2: Showing Error Messages in Forms

You can use useActionState to display error messages when a submission fails — super handy in forms with backend validation.

import { useActionState } from 'react';
import { addToCart } from './actions.js';

function AddToCartForm({ itemID, itemTitle }) {
  const [message, formAction] = useActionState(addToCart, null);

  return (
    <form action={formAction}>
      <h2>{itemTitle}</h2>
      <input type="hidden" name="itemID" value={itemID} />
      <button type="submit">Add to Cart</button>
      {message && <p className="error">{message}</p>}
    </form>
  );
}

// actions.js (server function)
"use server";

export async function addToCart(prevState, formData) {
  const itemID = formData.get('itemID');
  if (itemID === "1") {
    return "Added to cart";
  } else {
    return "Couldn't add to cart: the item is sold out.";
  }
}
Enter fullscreen mode Exit fullscreen mode

This pattern helps you handle and display server-side errors without extra boilerplate.


🔁 Example 3: Maintain Form State Across Pages

Using the optional permalink argument, useActionState can preserve form state even if the user navigates away and returns later.

import { useActionState } from 'react';
import { submitFeedback } from './actions.js';

function FeedbackForm() {
  const [feedback, formAction] = useActionState(submitFeedback, '', '/feedback');

  return (
    <form action={formAction}>
      <textarea name="feedback" placeholder="Your feedback" />
      <button type="submit">Submit</button>
      <p>{feedback}</p>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode

This ensures your form state survives across navigations — a great UX boost, especially in multi-step flows or persistent layouts.


⚠️ When Not to Use useActionState

Although this hook is powerful, it’s not the right fit for every use case:

  • List Keys: Don’t use it for React list keys — use stable, unique IDs from your data.
  • User-Controlled IDs: If you’re getting IDs or state from a database or API, you don't need useActionState.
  • Highly Dynamic UIs: If you need to update state on every render (like for animations or transient states), consider useRef or local useState instead.

✅ Final Thoughts

useActionState is a game-changer for handling forms in React, especially in apps using Server Components.

It gives you better control over form results, lets you manage error messages seamlessly, and supports persistent form state across routes.

If you’re already exploring React’s latest features, adding this hook to your toolkit is a no-brainer.

Happy coding!

Top comments (2)

Collapse
 
himanshu_code profile image
Himanshu Sorathiya

Did you just copy pasted When Not to use Section?

Collapse
 
joodi profile image
Joodi

Good catch! I wrote that based on stuff I’ve seen around, but yeah, it does sound a bit copy-pastey. I’ll clean it up a bit.

👋 Kindness is contagious

Engage with a wealth of insights in this thoughtful article, cherished by the supportive DEV Community. Coders of every background are encouraged to bring their perspectives and bolster our collective wisdom.

A sincere “thank you” often brightens someone’s day—share yours in the comments below!

On DEV, the act of sharing knowledge eases our journey and forges stronger community ties. Found value in this? A quick thank-you to the author can make a world of difference.

Okay