useActionState is a hook to let you initialize a state for a function such as a server action, and update that state based on the result of the action.

useActionState is a great match with server action because it allows you to interact with form even before JavaScript has been executed on the client side.

One powerful use of useActionState is to handle errors that may be generated during validation in the server action. In the following example, we use zod to perform schema validation. If the parse fails, our addProduct action returns the errors as the new state of the first parameter destructuring from useActionState.

actions.ts
const addSchema = z.object({
  name: z.string().min(1),
  description: z.string().min(1),
});
 
export const addProduct = async (prevState: unknown, formData: FormData) => {
  const result = addSchema.safeParse(Object.fromEntries(formData.entries()));
  
  if (!result.success) {
    return result.error.formErrors.fieldErrors;
  }
  
  // handle product adding
}

Even if we set the type of prevState to unknown, TypeScript is smart enough to know that the type of error is equal to result.error.formErrors.fieldErrors.

page.tsx
export const ProductForm = ({ product }: { product: Product | null }) => {
  const [error, action] = useFormState(addProduct, {});
 
  return (
    <form action={action} className="space-y-8">
      <div className="space-y-2">
        <Label htmlFor="name">Name</Label>
        <Input
          type="text"
          id="name"
          name="name"
          required
          defaultValue={product?.name}
        />
        {error.name && <div className="text-destructive">{error.name}</div>}
      </div>
      
      {/* Other inputs...*/}
  </form>
  );
}

References