next.js icon indicating copy to clipboard operation
next.js copied to clipboard

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

Open ColemanDunn opened this issue 2 years ago • 16 comments

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

ColemanDunn avatar Sep 13 '23 16:09 ColemanDunn

Where are you looking at warnings?

moonman239 avatar Sep 13 '23 20:09 moonman239

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

ColemanDunn avatar Oct 18 '23 19:10 ColemanDunn

getting the same warning in the same situation. Using Webstorm

casualWaist avatar Nov 02 '23 20:11 casualWaist

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.

kuskhan avatar Jan 14 '24 13:01 kuskhan

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.

ziyafenn avatar Jan 24 '24 11:01 ziyafenn

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

ColemanDunn avatar Jan 24 '24 16:01 ColemanDunn

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

decoursin avatar Apr 13 '24 05:04 decoursin

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 avatar Apr 29 '24 23:04 ColemanDunn

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

decoursin avatar Apr 30 '24 06:04 decoursin

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.

boar-is avatar Jun 28 '24 15:06 boar-is

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

decoursin avatar Jun 28 '24 15:06 decoursin

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

boar-is avatar Jun 28 '24 15:06 boar-is

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

decoursin avatar Jun 28 '24 16:06 decoursin

A very strong argument (especially for teams) 🤣

boar-is avatar Jun 28 '24 18:06 boar-is

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.

decoursin avatar Jun 28 '24 19:06 decoursin

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.

boar-is avatar Jun 28 '24 19:06 boar-is

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

freddy24-7 avatar Sep 12 '24 18:09 freddy24-7

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.

tusharsnx avatar Feb 07 '25 05:02 tusharsnx

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 avatar Feb 10 '25 18:02 ColemanDunn

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

ColemanDunn avatar Feb 10 '25 18:02 ColemanDunn

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.

jtouchto avatar Feb 18 '25 23:02 jtouchto

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

antspy avatar Mar 14 '25 08:03 antspy

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.

antspy avatar Mar 14 '25 08:03 antspy

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.

tusharsnx avatar Mar 14 '25 08:03 tusharsnx

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 avatar Mar 27 '25 19:03 ColemanDunn

@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 avatar Mar 28 '25 00:03 ColemanDunn

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

ColemanDunn avatar Mar 28 '25 00:03 ColemanDunn

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?

jtouch2 avatar Mar 28 '25 02:03 jtouch2

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 avatar Mar 28 '25 03:03 ColemanDunn

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

tusharsnx avatar Mar 28 '25 06:03 tusharsnx