Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Next TS plugin warning 'TS71007: Props must be serializable for components in the "use client" entry file' does not work with export default #55332

Open
1 task done
ColemanDunn opened this issue Sep 13, 2023 · 30 comments
Labels
bug Issue was opened via the bug report template.

Comments

@ColemanDunn
Copy link

ColemanDunn commented Sep 13, 2023

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

  • 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

@ColemanDunn ColemanDunn added the bug Issue was opened via the bug report template. label Sep 13, 2023
@moonman239
Copy link

Where are you looking at warnings?

@ColemanDunn
Copy link
Author

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

@casualWaist
Copy link

getting the same warning in the same situation. Using Webstorm

@kuskhan
Copy link

kuskhan commented Jan 14, 2024

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.

@ziyafenn
Copy link
Contributor

ziyafenn commented Jan 24, 2024

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.

@ColemanDunn
Copy link
Author

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

@decoursin
Copy link

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).

@ColemanDunn
Copy link
Author

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.

@decoursin
Copy link

@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.

@boar-is
Copy link
Contributor

boar-is commented Jun 28, 2024

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 export keyword, 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.

@decoursin
Copy link

No it doesn't make sense, if you're not using it from a server component.

@boar-is
Copy link
Contributor

boar-is commented Jun 28, 2024

If you use export, you can't control who's the caller. It can be any component, including RSC.

@decoursin
Copy link

I can because I'm the programmer. (same argument for teams)

@boar-is
Copy link
Contributor

boar-is commented Jun 28, 2024

A very strong argument (especially for teams) 🤣

@decoursin
Copy link

decoursin commented Jun 28, 2024

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.

@boar-is
Copy link
Contributor

boar-is commented Jun 28, 2024

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.

@freddy24-7
Copy link

Changing from "export default function" to exporting the component as a named export solved this for my case.

@tusharsnx
Copy link

tusharsnx commented Feb 7, 2025

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.

@ColemanDunn
Copy link
Author

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

@ColemanDunn
Copy link
Author

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

@jtouchto
Copy link

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.

@antspy
Copy link

antspy commented Mar 14, 2025

Going the other direction - is it possible to have an option to turn this from 'warning' to 'error'?

@antspy
Copy link

antspy commented Mar 14, 2025

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.

@tusharsnx
Copy link

tusharsnx commented Mar 14, 2025

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.

@ColemanDunn
Copy link
Author

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
Copy link
Author

@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

@ColemanDunn
Copy link
Author

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

@jtouch2
Copy link

jtouch2 commented Mar 28, 2025

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?

@ColemanDunn
Copy link
Author

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.

@tusharsnx
Copy link

@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".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issue was opened via the bug report template.
Projects
None yet
Development

No branches or pull requests