spetz83
spetz83

Reputation: 532

Using react-hook-form with NextJS causes hydration error

I am attempting to use react-hook-form for a super simple login form. Here is my "working" code:


"use client";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";

interface IFormInputs {
  email: string;
  derp: string;
}

const schema = yup
  .object({
    email: yup.string().required(),
    derp: yup.string().min(6).required(),
  })
  .required();

export default function Login() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<IFormInputs>({
    resolver: yupResolver(schema),
  });
  const onSubmit = (data: IFormInputs) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("email")} />
      {errors.email && <p>{errors.email?.message}</p>}

      <input {...register("derp")} />
      {errors.derp && <p>{errors.derp?.message}</p>}

      <input type="submit" />
    </form>
  );
}

Notice the second input is named "derp" and registered as "derp". With this code, the form renders, validation works as expected and form can be submitted correctly.

If I change the name of the second input to ANYTHING else, say... "password", I get:

Error: Hydration failed because the initial UI does not match what was rendered on the server.

Here is example code that throws that error. Again, please note that the only difference here is the name/registration of the second input.

"use client";
import { useForm } from "react-hook-form";
import { yupResolver } from "@hookform/resolvers/yup";
import * as yup from "yup";

interface IFormInputs {
  email: string;
  password: string;
}

const schema = yup
  .object({
    email: yup.string().required(),
    password: yup.string().min(6).required(),
  })
  .required();

export default function Login() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<IFormInputs>({
    resolver: yupResolver(schema),
  });
  const onSubmit = (data: IFormInputs) => console.log(data);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("email")} />
      {errors.email && <p>{errors.email?.message}</p>}

      <input {...register("password")} />
      {errors.password && <p>{errors.password?.message}</p>}

      <input type="submit" />
    </form>
  );
}

I am at a complete loss as to what the issue could be here. My first thought was "Is password like a reserved name or something?" So I tried switching to "passphrase" no luck.

Any help is appreciated.

Upvotes: 3

Views: 1599

Answers (1)

Jordi Mart&#237;
Jordi Mart&#237;

Reputation: 51

same here Im using storyBlok

 const FormInput: FC<BlokComponentModel<FormInputProps>> = ({ blok }) => {
  const { register } = useFormContext();
  const { type, name } = blok;

  // const [validationRules, setValidationRules] = useState<Record<string, any>>();
  
  const validationRules: Record<string, any> = {
    ...(blok.type === 'email' && {
      pattern: /^[a-z0-9,_%+-]+@[a-z0-9,-]+\.[a-z]{2,4}$/i,
    }),
    ...(blok.type === 'tel' && {
      pattern: /^[\+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,8}$/,
    }),
  };
  return (
    <div className="flex flex-col " {...storyblokEditable(blok)}>
      <div className="my-2 ml-0 flex flex-row items-center">
        <label className="pr-4 text-left">{blok.label}</label>
        <input
          className="h-10 w-full text-black"
          type={blok.type}
          placeholder={blok.placeholder}
          {...register(blok.name, validationRules)}
          name={blok.name}
        />
      </div>
      <div className="text-red-500">
        {blok.validators &&
          blok.validators.map((nestedBlok) => (
            <StoryblokComponent
              blok={nestedBlok}
              key={nestedBlok._uid}
              inputName={blok.name}
            />
          ))}
      </div>
    </div>
  );
};
export default FormInput;

Upvotes: 0

Related Questions