0

Error

Unhandled Runtime Error Error: Hydration failed because the initial UI does not match what was rendered on the server. See more info here: https://nextjs.org/docs/messages/react-hydration-error

Not sure why I'm getting this error, the following is my Login view route component, and I've tried returning just the <AuthForm />, but also tried 2 different ways of disabling SSR.

'use client';
import dynamic from 'next/dynamic';
// import AuthForm from '@/components/AuthForm';

// const DynamicAuthForm = dynamic(() => import('@/components/AuthForm'), {
//   ssr: false,
// });

const NoSSR = dynamic(() => import('@/components/AuthForm'), { ssr: false });

const LoginPage = () => {
  return (
    <div>
      <NoSSR />;
    </div>
  );
  // return <DynamicAuthForm />;
  // return <AuthForm />;
};

export default LoginPage;

And my AuthForm.tsx component:

'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import Link from 'next/link';

import callFetch from '@/utils/fetch/callFetch';

const apiUrl = process.env.NEXT_PUBLIC_API_DEV_LOGIN || '';

if (!apiUrl) {
  throw new Error('API URL is not defined');
}

function AuthForm() {
  const router = useRouter();
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [isSubmitting, setIsSubmitting] = useState(false);

  // Create a function to handle form submission
  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    setIsSubmitting(true);

    const formBody = JSON.stringify({ user: email, password });

    try {
      const response = await callFetch(apiUrl, 'POST', formBody);

      if (!response.ok) {
        setIsSubmitting(false);
        // TODO - handle error in notification alert
        throw new Error(
          `Login failed: ${response.status} - ${response.statusText}`
        );
      }

      console.log('login response:', response);
      setIsSubmitting(false);

      // Redirect to postings upon successful login
      router.push('/postings');
    } catch (error) {
      console.error('Error logging in:', error);
      setIsSubmitting(false);
      // Handle error, show error message, etc.
    }
  };

  return (
    <>
      <div className="login-container">
        {/* <div className="form"> */}
        <form onSubmit={handleSubmit}>
          Login to
          <Link href="/">
            <h1 className="logo">
              Bounty<strong>Jobs</strong>
            </h1>
          </Link>
          <div>
            <label htmlFor="email">Email</label>
            <input
              type="email"
              id="email"
              value={email}
              onChange={e => setEmail(e.target.value)}
              required
            />
          </div>
          <div>
            <label htmlFor="password">Password:</label>
            <input
              type="password"
              id="password"
              value={password}
              onChange={e => setPassword(e.target.value)}
              required
            />
          </div>
          <div className="actions">
            <button disabled={isSubmitting}>Submit</button>
          </div>
        </form>
        {/* </div> */}
      </div>
    </>
  );
}

export default AuthForm;

The Form markup

I don't see any issue anywhere in my markup:

return (
    <>
      <div className="login-container">
        {/* <div className="form"> */}
        <form onSubmit={handleSubmit}>
          Login to
          <Link href="/">
            <h1 className="logo">
              Bounty<strong>Jobs</strong>
            </h1>
          </Link>
          <div>
            <label htmlFor="email">Email</label>
            <input
              type="email"
              id="email"
              value={email}
              onChange={e => setEmail(e.target.value)}
              required
            />
          </div>
          <div>
            <label htmlFor="password">Password:</label>
            <input
              type="password"
              id="password"
              value={password}
              onChange={e => setPassword(e.target.value)}
              required
            />
          </div>
          <div className="actions">
            <button disabled={isSubmitting}>Submit</button>
          </div>
        </form>
        {/* </div> */}
      </div>
    </>
  );

1 Answer 1

1

The problem was my RootLayout of /login

Original code:

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body>
        <div>{children}</div>
      </body>
    </html>
  );
}

I removed the html and body tags here, which probably duplicated themselves since my app layout also has html and body.

This fixed the issue.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.