Home
Daily
Form Handling in React
December 14, 2025
1 min

Table Of Contents

01
useActionState
02
React Hook Form

Which should you use for forms in modern React?

  • useActionState (with Server Actions)
  • React Hook Form (RHF)

useActionState

Best for:

  • Simple forms that submit directly to the server
  • Example: Contact forms, login forms.

useFormStatus

The useFormStatus hook from React Server Actions provides form submission status (like pending) on the client.

import { useActionState, useFormStatus } from "react";
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? "Submitting..." : "Submit"}
</button>
);
}
export default function Form() {
const [state, formAction] = useActionState(createUser, { error: null });
return (
<form action={formAction}>
<input name="email" />
<SubmitButton />
{state.error && <p>{state.error}</p>}
</form>
);
}

React Hook Form

  • React Hook Form is a client-side form library focused on performance and flexibility.
  • Zod is a schema validation library that works well with RHF for defining form validation rules.

Best for:

  • Complex forms
  • Rich validation rules
  • Advanced UX (live errors, conditionals)

Zod Most Used in Real Forms

Use CaseZod Pattern
Required emailz.string().email()
Passwordz.string().min(6)
Usernamez.string().min(3).max(20)
Confirm password.refine((d) => d.pw === d.cpw)
Agez.number().int().positive()
Phonez.string().regex(/^\d{10}$/)
Terms checkboxz.literal(true)

Example:

import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
// 1. Define schema
const schema = z.object({
email: z.string().email("Invalid email"),
password: z.string().min(6, "Minimum 6 characters"),
});
type FormData = z.infer<typeof schema>;
export default function Form() {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<FormData>({
resolver: zodResolver(schema),
});
// 2. Submit handler
const onSubmit = async (data: FormData) => {
await fetch("/api/register", {
method: "POST",
body: JSON.stringify(data),
});
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("email")} placeholder="Email" />
{errors.email && <p>{errors.email.message}</p>}
<input type="password" {...register("password")} placeholder="Password" />
{errors.password && <p>{errors.password.message}</p>}
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? "Submitting..." : "Submit"}
</button>
</form>
);
}

Tags

#Daily

Share

Related Posts

Daily
Cookies, Sessions, and Local Storage
January 20, 2026
1 min
© 2026, All Rights Reserved.
Powered By

Social Media

githublinkedinyoutube