vercel / next.js

The React Framework
https://nextjs.org
MIT License
123.3k stars 26.32k forks source link

Next.js React Component Executing on Server Side Instead of Client Side #60683

Open UnnikrishnanBhargavakurup opened 6 months ago

UnnikrishnanBhargavakurup commented 6 months ago

Link to the code that reproduces this issue

https://github.com/UnnikrishnanBhargavakurup/my-minimal-nextjs-issue-reproduction

To Reproduce

  1. Start the application in development.
  2. Navigate to http://localhost:3000/contact.
  3. Check the server log.

You should observe "/contact" in the browser console, as it is a client-side component.

Current vs. Expected behavior

MobileNav.tsx is defined as a client component but appearing as server component.

Verify canary release

Provide environment information

Operating System:
  Platform: WSL 2
  Arch: 64-bit operating system, x64-based processor
  Version: Windows 11
Binaries:
  Node: v20.10.0
  npm: 10.2.3
  Yarn: N/A
  pnpm: 8.14.1
Relevant Packages:
  next: 14.0.5-canary.58
  eslint-config-next: 14.0.4
  react: ^18
  react-dom: ^18
  typescript: ^5
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Not sure

Which stage(s) are affected? (Select all that apply)

next dev (local)

Additional context

I am facing an issue with a React component in my Next.js application. The component is meant for a mobile navigation menu and uses certain hooks and modules. However, it seems to be executing on the server side instead of the client side. Here is the relevant code:

'use client'

import { ArrowRight, Menu } from 'lucide-react'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { useEffect, useState } from 'react'

const MobileNav = () => {
  const [isOpen, setOpen] = useState<boolean>(false)

  const toggleOpen = () => setOpen((prev) => {
    console.log("toggling menu");
    return !prev
  })

  const pathname = usePathname()
  console.log(pathname);

  useEffect(() => {
    console.log("calling use effect");
    if (isOpen) toggleOpen()
  }, [isOpen, pathname])

  const closeOnCurrent = (href: string) => {
    if (pathname === href) {
      console.log("closing the menu");
      toggleOpen()
    }
  }

  return (
    <div className='sm:hidden'>
      <Menu
        onClick={toggleOpen}
        className='relative z-50 h-5 w-5 text-zinc-700'
      />

      {isOpen ? (
        <div className='fixed animate-in slide-in-from-top-5 fade-in-20 inset-0 z-0 w-full'>
          <ul className='absolute bg-white border-b border-zinc-200 shadow-xl grid w-full gap-3 px-10 pt-20 pb-8'>
            <li>
              <Link
                onClick={() =>
                  closeOnCurrent('/contact')
                }
                className='flex items-center w-full font-semibold'
                href='/contact'>
                Contact Us
              </Link>
            </li>
          </ul>
        </div>
      ) : null}
    </div>
  )
}

export default MobileNav

Issue: The problem is that when I run my application, the useEffect and usePathname hooks don't seem to behave as expected, and the component appears to be running on the server side.

image

Why might this code be executing on the server side despite the intention for client-side execution?

I would appreciate any insights or suggestions on how to troubleshoot and resolve this issue. Thank you!

rinald commented 6 months ago

@UnnikrishnanBhargavakurup There is nothing wrong with your component. By default, Next.js will pre-render client components on the server. This means that it will generate the initial HTML on the server (hence, why you see the path printed) and the component is later hydrated on the client. useEffect only runs in the browser

You can learn more about the rendering behavior here: https://nextjs.org/docs/app/building-your-application/rendering/client-components#how-are-client-components-rendered

UnnikrishnanBhargavakurup commented 6 months ago

Got it, please close this issue.

Thanks!