[BUG] - createContext only works in Client Components
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
- Update nextjs to v16
- Start
next dev - Open a non client component
- Error
Expected behavior
Work like before
Screenshots or Videos
Operating System Version
Windows
Browser
Firefox
- Are you using
npx @next/codemod@canary upgrade latestto upgrade to next 16? - 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 Nop upgarded it manually after
npx @next/codemod@canary upgrade latestfailed - 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>
</>
);
}
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.
Not really, its company code. I already stripped away confidential infos.
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.
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;
It seems like importing from global is breaking, but individual works
import { Chip } from "@heroui/react"; // Breaking
import { Chip } from "@heroui/chip"; // Working
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 clientdirective. - You don't need to add
use clientdirective 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 clientto the global package@heroui/react. That's why when you importSkeletonfrom@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.
Just want to point out that it was working prior to the NextJS upgrade (v15).
@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...
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?
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.
Any update @wingkwong ?
@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.
@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 Thanks for the findings. Will evaluate it later.
@wingkwong maybe reopen the issue?
Facing the same issue, and next [command] --webpack worked for me.
@wingkwong any idea for a fix?
Also btw: the --webpack also fixed it for me