tailwindlabs / tailwindui-issues

Bug fixes and feature request tracking for Tailwind UI.
233 stars 4 forks source link

Next.js className function problem #1575

Closed theeldarka closed 3 months ago

theeldarka commented 4 months ago

What component (if applicable)

Describe the bug I guess, Next.js has upgraded and now they don't support functions to className attribute on the client side

To Reproduce Steps to reproduce the behavior:

  1. Start clean Next.js v14.2.3
  2. Copy & Paste React code from tailwindui.com
  3. Now, there is an error
Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server". Or maybe you meant to call this function rather than return it.
  <... as="header" className={function className} children=...>
HanSeonWoo commented 3 months ago

@theeldarka Could this be because it's not a client component? I can't do button actions on the server component, so I have to use the I need to put 'use client' at the top of the page tailwindui don't consider Next, so I have to take care of whether it's a client component or not.

RobinMalfait commented 3 months ago

Hey!

Unfortunately you can't pass functions/callbacks to components in server components because they are not serializable. In this case you can do a few things:

  1. Convert the component to a client component. You can do this by adding 'use client' at the top of the file.
  2. If you are using the latest Headless UI version, then you can also use the data attributes that we expose.

For example, this:

<Popover
  as="header"
  className={({ open }) =>
    classNames(
      open ? 'fixed inset-0 z-40 overflow-y-auto' : '',
      'bg-white shadow-sm lg:static lg:overflow-y-visible'
    )
  }
>

... turns into something like this:

<Popover
  as="header"
  className="data-[open]:fixed data-[open]:inset-0 data-[open]:z-40 data-[open]:overflow-y-auto bg-white shadow-sm lg:static lg:overflow-y-visible"
  {/*        ---------------------------------------------------------------------------------- */}
>

Notice how each class that used to be added based on the open state, now is added based on the data-[open]: Tailwind variant which uses the open data attribute.

In case you are showing elements conditionally, you can also use group-data in Tailwind CSS and apply the hidden class:

So code like this:

<Popover>
  {open ? (
    <XMarkIcon className="block h-6 w-6" aria-hidden="true" />
  ) : (
    <Bars3Icon className="block h-6 w-6" aria-hidden="true" />
  )}
</Popover>

... turns into this:

<Popover className="group">
  <XMarkIcon className="group-data-[open]:block hidden h-6 w-6" aria-hidden="true" />
  <Bars3Icon className="group-data-[open]:hidden block h-6 w-6" aria-hidden="true" />
</Popover>

All that said, maybe Next.js and React will one day figure out on how to serialize functions, but for now this is what you have to do.

We will also very likely convert our Tailwind UI components to make use of these data attributes which will simplify the components more.

Hope this helps!