Next TS plugin warning 'TS71007: Props must be serializable for components in the "use client" entry file' does not work with export default
Link to the code that reproduces this issue or a replay of the bug
https://codesandbox.io/p/sandbox/brave-northcutt-3h899t?file=%2Fapp%2Fpage.tsx%3A22%2C1
To Reproduce
type myProps = {
myFunc: () => void;
};
const myComponent = ({ myFunc }: myProps) => {
myFunc();
return <></>;
};
export default myComponent;
export const myComponent2 = ({ myFunc }: myProps) => {
myFunc();
return <></>;
};
export function myComponent3({ myFunc }: myProps) {
myFunc();
return <></>;
}
only myComponent2 and myComponent3 show the warning.
Current vs. Expected behavior
Warning should also show for myComponent
Verify canary release
- [X] I verified that the issue exists in the latest Next.js canary release
Provide environment information
Operating System:
Platform: darwin
Arch: arm64
Version: Darwin Kernel Version 23.0.0: Thu Aug 17 21:23:05 PDT 2023; root:xnu-10002.1.11~3/RELEASE_ARM64_T6000
Binaries:
Node: 20.3.1
npm: 10.1.0
Yarn: 1.22.19
pnpm: 8.7.4
Relevant Packages:
next: 13.4.19
eslint-config-next: 13.4.19
react: 18.2.0
react-dom: 18.2.0
typescript: 5.2.2
Next.js Config:
output: standalone
Which area(s) are affected? (Select all that apply)
App Router
Additional context
Code sandbox is a little redundant but see the components.tsx file inside app folder for the same code as above. The plugin code for this warning is here: https://github.com/vercel/next.js/blob/canary/packages/next/src/server/typescript/rules/client-boundary.ts
Where are you looking at warnings?
Where are you looking at warnings?
sorry I missed this. what do you mean exactly? The warnings show up in my IDE if that is what you mean
getting the same warning in the same situation. Using Webstorm
I have the same issue in WebStorm. It is annoying. I am not sure about the warning, but it seems like there's no problem when I build the project for deployment.
Plus, the problem will disappear after changing the code above in this way.
from
const myComponent = ({ myFunc }: myProps) => {
myFunc();
return <></>;
};
to
const myComponent = (myWorkingProps: myProps) => {
myWorkingProps.myFunc();
return <></>;
};
myWorkingProps can be any arbitrary word.
I have this warning on VSCode too.
Another annoying thing about this warning is that if the parent component is client component, removing the "use client" directive from the child component will resolve this warning, although the child component still will be a client component.
removing the
"use client"directive from the child component will resolve this warning,
That is the intended fix for this warning
although the child component still will be a client component.
That is how Next.js works
To me, it is important that I label all client components with the 'use client' directive.
Our editors should either be smart enough to figure out that only parent components are also client components, so that there is no error, or else our IDEs shouldn't complain at all.
In my opinion, Next.js should allow us to turn off this warning TS71007 in tsconfig.json (or somewhere else).
To me, it is important that I label all client components with the 'use client' directive.
Our editors should either be smart enough to figure out that only parent components are also client components, so that there is no error, or else our IDEs shouldn't complain at all.
In my opinion, Next.js should allow us to turn off this warning TS71007 in
tsconfig.json(or somewhere else).
This is incorrect usage. Components marked with the "use client" should not (because they cannot when used by a server component) take in non-serializable props. Given "use client" is meant to mark client components to be usable by server components, turning off this warning would only cause harm and you would get the Error: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server". when trying to do something like pass a function to the client component from a server component.
@ColemanDunn no you don't get what I'm saying. If a client component is always only used by other client components, its arguments don't need to be serializable.
when trying to do something like pass a function to the client component from a server component.
Exactly. But if you have client components that are never called from other server components, because they're only called from other client components, then the warning should never appear.
For me, removing the export keyword was a solution:
'use client'
export function Foo() {
return <Bar nonSerializable={...}/>
}
function Bar() { // remove export from the child component
return ...
}
This rule actually makes sense:
- If you export a component, then any server component can import and use it (accidentally or not). You can't be 100% sure.
- By removing the
exportkeyword, you tell that the component will not leave this module.
Or if you want to keep the export ("you-know-what-you're-doing"), just put the @ts-expect-error directive.
No it doesn't make sense, if you're not using it from a server component.
If you use export, you can't control who's the caller. It can be any component, including RSC.
I can because I'm the programmer. (same argument for teams)
A very strong argument (especially for teams) 🤣
Maybe for you and your teams it wouldn't be 🤔 haha
And a computer can make a graph and determine if it's being used by a server component and only then show the alert - otherwise there isn't an error.
My initial idea was about external code. Taking libraries in example, authors don’t know how and by whom their code will be called. Respectively, their computers are not able to build a graph.
Changing from "export default function" to exporting the component as a named export solved this for my case.
The plugin fails to account for the fact that "use client" inside a client boundary is essentially a no-op, and doesn't cause any real problems. It's suppose to mark boundaries only when used directly under a RSC and not anywhere else. I understand that the plugin is not smart enough to figure it out today, but why isn't it experimental? We should have a flag to disable it. It's nice that next build doesn't fail due to these warnings.
And a computer can make a graph and determine if it's being used by a server component and only then show the alert - otherwise there isn't an error.
Sounds like a lot of work for a custom compiler plugin just to silence a warning caused by the developer misusing use client
but why isn't it experimental?
Because it is working as intended except for the title of this issue
It's nice that next build doesn't fail due to these warnings.
tbh it probably should be because if you were to pass a function to one of these from a server component you would get a runtime error
I just ran into this message.
Took me a while to wrap my head around it because I like being verbose and adding 'use client' to every client side component. However if me labeling a client side component as being explicitly 'use client' is misusing 'use client' I feel like there is a disconnect somewhere.
If the assumption is I only put 'use client' on boundaries, ie entry points to the client side code, we loose verbosity. A new developer on my team could try using a child component inside of a server component, leading to a different message, which is a run time error.
Specifically this one:
You're importing a component that needs useState. This React hook only works in a client component. To fix, mark the file (or its parent) with the "use client" directive.
Verbosity eliminates the need to trace multiple components to confirm client-side execution.
So if the disconnect is this Expected: 'use client' is only in the entry point to the client side Me: using 'use client' in every client side component I make
We either need to
Move the warning to the server component that passes a function as a prop.
Or
Provide a "use client child" (could be any name) directive to explicitly mark a component as a child of a client component.
Going the other direction - is it possible to have an option to turn this from 'warning' to 'error'?
Alternatively, it would be cool if there was a way to mark components as 'children of clients only' to get around this issue completely. Components marked as this can only be used by client components, a server cannot access it.
I'm convinced that "use client" is a bad semantic for marking boundaries.
Boundary markers belong within server components. That's where I, as a developer, exactly know that a client component is a boundary component. We need a way to tell this to the linter, and we probably already have a better syntax to do that, i.e. import attributes.
// layout.tsx
import { FancyButton } from "./fancy-button.tsx" with { use: "client" }
By looking at the import attribute, linters can restrict the passing of non-serializable props to FancyButton.
I just ran into this message.
Took me a while to wrap my head around it because I like being verbose and adding 'use client' to every client side component. However if me labeling a client side component as being explicitly 'use client' is misusing 'use client' I feel like there is a disconnect somewhere.
If the assumption is I only put 'use client' on boundaries, ie entry points to the client side code, we loose verbosity. A new developer on my team could try using a child component inside of a server component, leading to a different message, which is a run time error.
Specifically this one: You're importing a component that needs
useState. This React hook only works in a client component. To fix, mark the file (or its parent) with the"use client"directive.Verbosity eliminates the need to trace multiple components to confirm client-side execution.
So if the disconnect is this Expected: 'use client' is only in the entry point to the client side Me: using 'use client' in every client side component I make
We either need to
Move the warning to the server component that passes a function as a prop.
Or
Provide a "use client child" (could be any name) directive to explicitly mark a component as a child of a client component.
I'm convinced that "use client" is a bad semantic for marking boundaries.
Boundary markers belong within server components. That's where I, as a developer, exactly know that a client component is a boundary component. We need a way to tell this to the linter, and we probably already have a better syntax to do that, i.e. import attributes.
// layout.tsx
import { FancyButton } from "./fancy-button.tsx" with { use: "client" } By looking at the import attribute, linters can restrict the passing of non-serializable props to
FancyButton.
How did this issue about a Nextjs typescript plugin devolve into making suggestions to core features of React? I suggest taking these discussion to the React repo if you have issues however I am not sure how receptive they will be to changes to a long-worked on and thought-out feature. This issue relates to the current behavior offered by the framework’s third-party dependency
@ColemanDunn no you don't get what I'm saying. If a client component is always only used by other client components, its arguments don't need to be serializable.
@decoursin I get exactly what you are saying. You explained how you mark every component with client functionality with use client, I explained how that is incorrect usage, to which you replied by saying I don't get what you are saying then doubled down on using use client incorrectly.
Next.js should allow us to turn off this warning
In fact you can disable their custom typescript plugin as shown here but I wouldn't recommend it as one of its stated purposes is "Ensuring the use client directive is used correctly." which you claimed to not be doing
Changing from "export default function" to exporting the component as a named export solved this for my case.
Thank you for your comment however this is already shown in the example the issue description
How did this issue about a Nextjs typescript plugin devolve into making suggestions to core features of React? I suggest taking these discussion to the React repo if you have issues however I am not sure how receptive they will be to changes to a long-worked on and thought-out feature. This issue relates to the current behavior offered by the framework’s third-party dependency
Neither of the examples you provided were suggesting changes to core react?
How did this issue about a Nextjs typescript plugin devolve into making suggestions to core features of React? I suggest taking these discussion to the React repo if you have issues however I am not sure how receptive they will be to changes to a long-worked on and thought-out feature. This issue relates to the current behavior offered by the framework’s third-party dependency
Neither of the examples you provided were suggesting changes to core react?
what do you call the following?
Provide a "use client child" (could be any name) directive to explicitly mark a component as a child of a client component.
@ColemanDunn
How did this issue about a Nextjs typescript plugin devolve into making suggestions to core features of React?
Those words represent the conclusion I came to, and aren't suggestions for Nextjs. I understand that "use client" is a standard, and I should've made those observations in React's forum, but it doesn't harm to say it here, I guess.
I am not sure how receptive they will be to changes to a long-worked on and thought-out feature. This issue relates to the current behavior offered by the framework’s third-party dependency.
Yeah, I agree. We need a change in the spec to be able to improve the DX in this area. I think this issue can be closed as "by-design".