nextui icon indicating copy to clipboard operation
nextui copied to clipboard

[BUG] - createContext only works in Client Components

Open gltched-usr opened this issue 1 month ago • 18 comments

HeroUI Version

2.8.5

Describe the bug

I just upgraded my nextjs to v16 and now every non client component fails with:

createContext only works in Client Components. Add the "use client" directive at the top of the file to use it. Read more: https://nextjs.org/docs/messages/context-in-server-component

Your Example Website or App

No response

Steps to Reproduce the Bug or Issue

  1. Update nextjs to v16
  2. Start next dev
  3. Open a non client component
  4. Error

Expected behavior

Work like before

Screenshots or Videos

Image

Operating System Version

Windows

Browser

Firefox

gltched-usr avatar Nov 10 '25 17:11 gltched-usr

  1. Are you using npx @next/codemod@canary upgrade latest to upgrade to next 16?
  2. which non client components are you using? so far I'm not able to reproducible the issue. Could you please provide a minimal reproducible environment for us to check? Thanks.

wingkwong avatar Nov 11 '25 03:11 wingkwong

  1. @wingkwong Nop upgarded it manually after npx @next/codemod@canary upgrade latest failed
  2. This for example
import { Button, Link } from "@heroui/react";
import { IconBrandTwitch } from "@tabler/icons-react";
import ErrorToast from "@components/errorToast";
import { getBaseUrl, isPreview } from "@actions/utils";

export default async function Login({ searchParams }: { searchParams: Promise<{ [key: string]: string | string[] | undefined }> }) {
	const scopes: string[] = ["user:read:email", "channel:read:redemptions", "channel:manage:redemptions", "user:read:chat", "user:write:chat", "user:bot", "channel:bot"];

	let state = Buffer.from(new Date().toISOString()).toString("base64");

	const baseUrl = await getBaseUrl();
	let callbackUrl = new URL("/callback", baseUrl);

	const authLink = new URL("https://id.twitch.tv/oauth2/authorize");
	if ((await isPreview()) && process.env.PREVIEW_CALLBACK_URL) {
		state = Buffer.from(JSON.stringify({ initiator: callbackUrl, date: new Date().toISOString() })).toString("base64");
		callbackUrl = new URL(process.env.PREVIEW_CALLBACK_URL);
	}

	authLink.searchParams.append("client_id", process.env.TWITCH_CLIENT_ID || "");
	authLink.searchParams.append("redirect_uri", callbackUrl.toString());
	authLink.searchParams.append("response_type", "code");
	authLink.searchParams.append("scope", scopes.join(" "));
	authLink.searchParams.append("force_verify", "true");
	authLink.searchParams.append("state", state);

	const { error, errorCode } = await searchParams;
	return (
		<>
			<ErrorToast error={error as string} errorCode={errorCode as string} />
			<div className='min-h-screen min-w-screen flex items-center justify-center bg-gradient-to-br from-primary-800 to-primary-400'>
				<div className='flex flex-col items-center'>
					<Button as={Link} href={authLink.toString()} startContent={<IconBrandTwitch color='#8956FB' />} variant='faded' size='lg' color='default' aria-label='Login with Twitch'>
						Login with Twitch
					</Button>
				</div>
			</div>
		</>
	);
}

gltched-usr avatar Nov 11 '25 12:11 gltched-usr

Do you have a repo or a sandbox to share? The partial code doesn't help us to investigate the issue. I've tested with both Button and Link and didn't reproduce.

wingkwong avatar Nov 11 '25 13:11 wingkwong

Not really, its company code. I already stripped away confidential infos.

gltched-usr avatar Nov 11 '25 14:11 gltched-usr

You don't need to share the actual code. I was asking for a minimal reproducible environment only. Also have you tried running codemod from next to see if there is missing changes from the upgrade? From my previous testing, tsconfig.json will be also updated as well.

wingkwong avatar Nov 11 '25 15:11 wingkwong

I'm also getting this issue after upgrade. The page has minimal code. (Might be related to React 19)

import { Chip } from "@heroui/react";

function Page() {
  return (
    <div>
      <Chip />
    </div>
  );
}

export default Page;
Image

It seems like importing from global is breaking, but individual works

import { Chip } from "@heroui/react"; // Breaking
import { Chip } from "@heroui/chip"; // Working

mengqing avatar Nov 13 '25 03:11 mengqing

Actually it's not related to nextjs upgrade nor react 19.

  • NextJS by default uses server component.
  • HeroUI components use React hooks (only a few don't). You cannot use a react hook in a server component unless you make it as a client component. You can do that by adding use client directive.
  • You don't need to add use client directive in an individual package (e.g. @heroui/skeleton) because we add it to those required components internally.
  • Not all components are client components. Therefore, we cannot add use client to the global package @heroui/react. That's why when you import Skeleton from @heroui/react, you need to add it by yourself. Otherwise, you will get that error.

Therefore, you can either add use client if you want to use global package or just use an individual package.

wingkwong avatar Nov 13 '25 04:11 wingkwong

Just want to point out that it was working prior to the NextJS upgrade (v15).

mengqing avatar Nov 13 '25 04:11 mengqing

@wingkwong as @mengqing pointed it out, it worked before upgrading. And I hope "just convert every server component to client components" is not the only solution you give us...

gltched-usr avatar Nov 13 '25 09:11 gltched-usr

Not sure if I understood correctly. From my understanding, based on @mengqing's statement, the given example works fine in next 15 but not after upgrade (next 16). And I tested it on next-app-template with both next 15 and next 16. Using import { Chip } from "@heroui/react"; will throw the reported error and work fine after adding the use client directive. The behaviour is expected as explained in my previous comment.

import { Chip } from "@heroui/react";

function Page() {
  return (
    <div>
      <Chip />
    </div>
  );
}

export default Page;

Or can you guys provide 2 minimal reproducible sandbox - one working fine before upgrade and another one failing after upgrade?

wingkwong avatar Nov 13 '25 14:11 wingkwong

But the point is: It worked before. This issue wouldn't be open if the code didn't worked before. And converting everything into "use client" isn't an option for existing projects.

gltched-usr avatar Nov 14 '25 06:11 gltched-usr

Any update @wingkwong ?

gltched-usr avatar Dec 03 '25 23:12 gltched-usr

@gltched-usr As requested in my previous comment, I need 2 minimal reproducible sandbox - one working fine before upgrade and another one failing after upgrade.

wingkwong avatar Dec 04 '25 05:12 wingkwong

@gltched-usr @mengqing @wingkwong I found a temporary fix for this problem. Just add --webpack behind the dev and build command:

next dev --webpack

Next.js v16 uses turbopack by default (instead of webpack): Turbopack by default.

If you disable Turbopack, everything works again. When running with Turbopack, some HeroUI modules seem to be incorrectly treated as Server Components, which causes code that calls createContext to run in the RSC environment. With Webpack, the same code works as expected, so this looks like a Turbopack-specific bundling or module classification issue.

Possible related issues: #3967 #3723

@wingkwong You think its possible to fix this on heroui's side? With the change to use turbopack by default starting next v16, they send a signal to make the switch from webpack to turbopack, maybe even dropping support for webpack completly. I think Turbopack compatibility will beocme increasing more important

TheDanniCraft avatar Dec 04 '25 08:12 TheDanniCraft

@TheDanniCraft Thanks for the findings. Will evaluate it later.

wingkwong avatar Dec 04 '25 09:12 wingkwong

@wingkwong maybe reopen the issue?

gltched-usr avatar Dec 04 '25 19:12 gltched-usr

Facing the same issue, and next [command] --webpack worked for me.

SiegeSailor avatar Dec 07 '25 16:12 SiegeSailor

@wingkwong any idea for a fix?

Also btw: the --webpack also fixed it for me

gltched-usr avatar Dec 07 '25 22:12 gltched-usr