Next.js Discord

Discord Forum

Do not reset form fields on action submit

Unanswered
Xiasi Dog posted this in #help-forum
Open in Discord
Xiasi DogOP
Hello, I was just wondering. I have a login form that contains username and password fields. When the user submits the form, it runs a simple server action that checks the validity of the credentials. If they aren't valid it returns an object with an error field which I then read using the useActionState hook.

My question is, is there a way to prevent the submit action from resetting the form? I don't want the need for the user to retype all their credentials when they enter something incorrectly.

24 Replies

Sandwich Tern
I don't know your code but can't you prevent it by using onSubmit on the form ?
And the smth like event.preventDefault()
Sandwich Tern
Can you share your code ?
It sounds like the page is reloading, so @Sandwich Tern's suggestion of preventDefault is likely the fix here. But also, code to look at will help us 😁
Asiatic Lion
I make the form data part of the state and have the action return it. Then set defaultValue on all the the fields from that.
Xiasi DogOP
Is the button supposed to have a type="submit"?
This is how the form component looks like:
"use client";

const SignInForm = () => {
  const [state, formAction] = useActionState(handleUsernamePasswordLogin, { error: "" });
  const { pending } = useFormStatus();

  const [showPassword, setShowPassword] = useState(false);

  return (
    <form className="mt-8 flex flex-col pb-4 md:pb-8" action={formAction}>
      {/* Username/Email Input Field */}
      <Input
        label="Username or Email"
        placeholder="Enter your username or email"
        name="username"
        autoComplete="username"
        required
        iconLeft={<User2Icon className="icon text-secondary" />}
      />

      {/* Password Input Field */}
      <Input
        label="Password"
        placeholder="Fill me!"
        name="password"
        type={showPassword ? "text" : "password"}
        autoComplete="current-password"
        error={state.error}
        required
        inputClass={!showPassword && "tracking-widest placeholder:tracking-normal"}
        iconLeft={<KeyRoundIcon className="icon text-secondary" />}
        iconRight={
          <button
            type="button"
            className="-mr-2 select-none p-2"
            onClick={() => setShowPassword(!showPassword)}>
            {showPassword ? (
              <EyeIcon className="icon text-secondary" />
            ) : (
              <EyeOffIcon className="icon text-tertiary" />
            )}
          </button>
        }
      />

      {/* Reset Password Trigger */}
      <Link href={""} className="-mt-5 self-end text-sm font-medium text-brand">
        Forgot password?
      </Link>

      {/* Sign In Button */}
      <button
        className="btn primary small brand mt-4 disabled:opacity-40"
        type="submit"
        disabled={pending}>
        Sign in
        <MoveRightIcon className="btn-arrow" />
      </button>
    </form>
  );
};
I'm also using the canary branch of react, react-dom and next
Yeah - looks like a preventDefault issue. I would:

  const handleSubmit = (event) => {
    event.preventDefault();
    formAction(new FormData(event.target));
  };

  return (
    <form className="mt-8 flex flex-col pb-4 md:pb-8" onSubmit={handleSubmit}>


Instead. So you call a local method, cancel any other form actions, and then pass off to formAction
@Xiasi Dog This is how the form component looks like: tsx "use client"; const SignInForm = () => { const [state, formAction] = useActionState(handleUsernamePasswordLogin, { error: "" }); const { pending } = useFormStatus(); const [showPassword, setShowPassword] = useState(false); return ( <form className="mt-8 flex flex-col pb-4 md:pb-8" action={formAction}> {/* Username/Email Input Field */} <Input label="Username or Email" placeholder="Enter your username or email" name="username" autoComplete="username" required iconLeft={<User2Icon className="icon text-secondary" />} /> {/* Password Input Field */} <Input label="Password" placeholder="Fill me!" name="password" type={showPassword ? "text" : "password"} autoComplete="current-password" error={state.error} required inputClass={!showPassword && "tracking-widest placeholder:tracking-normal"} iconLeft={<KeyRoundIcon className="icon text-secondary" />} iconRight={ <button type="button" className="-mr-2 select-none p-2" onClick={() => setShowPassword(!showPassword)}> {showPassword ? ( <EyeIcon className="icon text-secondary" /> ) : ( <EyeOffIcon className="icon text-tertiary" /> )} </button> } /> {/* Reset Password Trigger */} <Link href={""} className="-mt-5 self-end text-sm font-medium text-brand"> Forgot password? </Link> {/* Sign In Button */} <button className="btn primary small brand mt-4 disabled:opacity-40" type="submit" disabled={pending}> Sign in <MoveRightIcon className="btn-arrow" /> </button> </form> ); };
honestly the code looks fine for me. could you reproduce this bug in a minimal reproduction repository so i can clone and test it locally?
Yeah, if it's not the preventDefault either (thanks for the info @joulev!) then it's looking like it should work here too.
Sandwich Tern
Maybe you can try smth simple to see if this is the action handleUsernamePasswordLogin that make this issue. Just try to implement smth like
const handleSubmit = () => {
console.log("submit")
};

return (
<form className="mt-8 flex flex-col pb-4 md:pb-8" onSubmit={handleSubmit}>

and then tell us if it still reset the form
I'm stumped 🤷‍♂️ The docs say useActionState should handle the preventDefault, but checking your code out, I see the same error, and adding a handler, I see the error go away. Where do you see the complaint that this is not the recommended use of actions?
I've just implemented the example from https://react.dev/reference/react/useActionState and added a text input field. I'm also seeing that one reset after the action runs. It looks like preventDefault is indeed initiated by useActionState, but that form fields are reset afterwards. I can't see anything in the documentation about the default state of the form after submission, or how to override it. I suspect for your use case, it would be better to stick with onSubmit and call formAction from that handler.
@Xiasi Dog Here you go. This should be a minimal reproduction. https://github.com/vdemcak/nextjs-form-minimal
appears to be a react bug. if you downgrade to next 14 and react 18.3.1 (and use useFormState instead of useActionState, since useActionState doesn't work in 18.3.1), the issue goes away
i'll dig deeper to see whether this is a bug or an intentional decision – i certainly don't want my forms to automatically reset, either
yes, [it is an intentional feature](https://github.com/facebook/react/pull/28804) and there are more discussions against this feature in [this issue](https://github.com/facebook/react/issues/29034).

tl;dr: either use controlled inputs, or use onSubmit
+function handleSubmit(event) {
+  event.preventDefault();
+  const formData = new FormData(event.target);
+  startTransition(() => action(formData));
+}

...
-<form action={action}>
+<form onSubmit={handleSubmit}>


i don't agree with this decision at all but it is what it is, unfortunately.
thats so weird as so much of the coolness of server actions were that they dont reset fields (ie revalidatePath)