techmeowt
techmeowt

Reputation: 237

Error using 'use client' in Prismic CMS and Nextjs 14 with app router

I am building a website using Prismic CMS and Nextjs 14. I followed this tutorial which resulted in a successful website with no errors. On my new project's website, I'm getting an error when trying to import my <NavBar /> component into my layout.js file. My goal with the nav is to conditionally render CSS classes for active and inactive nav links. I don't get this error every time, but when I do, the error is:

enter image description here

I Googled the error, and I found this article. I tried putting the <NavBar /> code directly into my layout.js file. The page loaded fine, but I'm still getting that same error from time to time. I think that it has something to do with useRouter() and usePathname() because the tutorial didn't use that, but I'm not sure. I also read the Nextjs docs regarding client and server components and had trouble understanding.

This is the code from my layout.js file:

'use client'
import { PrismicPreview, PrismicNextLink, PrismicNextImage } from '@prismicio/next';
import { repositoryName, createClient } from '@/prismicio';
import { usePathname, useRouter } from 'next/navigation';
import './globals.css';
// import NavBar from './components/NavBar';
// import Footer from './components/Footer';

export default function RootLayout({ children }) {
  return (
    <html lang='en'>
      <body>
        <NavBar />
        {children}
        <PrismicPreview repositoryName={repositoryName} />
        {/* <Footer /> */}
      </body>
    </html>
  );
}

async function NavBar() {
  const pathname = usePathname();
  const router = useRouter();
  const client = createClient();
  const nav = await client.getSingle('navigation_menu');

  const inactive = 'hover:border-purpleDefault hover:border-b-2';
  const active = 'border-lavender border-b-2';

  return (
    <div className='flex justify-center items-center font-semibold max-w-full mx-auto py-2'>
      <div className='container flex justify-between'>
        <span className='text-2xl leading-6 font-logo flex flex-row items-center'>
          {nav.data.company_name}
          <PrismicNextImage
            field={nav.data.company_logo}
            className='h-20 w-20 ml-4'
          />
        </span>
        <ul className='flex items-center text-2xl'>
          {nav.data.menu_items.map((item) => {
            return (
              <li key={JSON.stringify(item)}>
                <PrismicNextLink
                  field={item.link}
                  className={`no-underline ${(router.pathname = item.link ? active : inactive)}`}
                >
                  {item.label}
                </PrismicNextLink>
              </li>
            );
          })}
        </ul>
      </div>
    </div>
  );
}

The <NavBar /> code used in the layout.js file is the exact same code that I used when the <NavBar /> was in the components folder.

This is my folder structure that's relevant to this post:

app
|
 —— api
 —— components
|
 —— slice-simulator
globals.css
layout.js
page.js

Upvotes: 0

Views: 414

Answers (1)

moonstar-x
moonstar-x

Reputation: 1168

The issue in here is that you're mixing use client with a component that uses await inside.

async/await components are always Server components, which means that you can't mix hooks (which require client components) and async/await (which require server components).

Your best solution is to move your NavBar component to its own separate file with the 'use client' directive to allow the use of hooks inside and have the data contained in the nav variable to be passed by props.

If you don't want to pollute your layout with client fetches you can do something like this:

  1. In a file named NavBarContent.jsx:
'use client';

export default function NavBarContent({ nav }) {
  const pathname = usePathname();
  const router = useRouter();

  const inactive = 'hover:border-purpleDefault hover:border-b-2';
  const active = 'border-lavender border-b-2';

  return (
    <div className='flex justify-center items-center font-semibold max-w-full mx-auto py-2'>
      <div className='container flex justify-between'>
        <span className='text-2xl leading-6 font-logo flex flex-row items-center'>
          {nav.data.company_name}
          <PrismicNextImage
            field={nav.data.company_logo}
            className='h-20 w-20 ml-4'
          />
        </span>
        <ul className='flex items-center text-2xl'>
          {nav.data.menu_items.map((item) => {
            return (
              <li key={JSON.stringify(item)}>
                <PrismicNextLink
                  field={item.link}
                  className={`no-underline ${(router.pathname = item.link ? active : inactive)}`}
                >
                  {item.label}
                </PrismicNextLink>
              </li>
            );
          })}
        </ul>
      </div>
    </div>
  );
}
  1. In a file named NavBar.jsx:
export default async function NavBar() {
  const client = createClient();
  const nav = await client.getSingle('navigation_menu');

  return (
    <NavBarContent nav={nav} />
  );
}

Upvotes: 1

Related Questions