Next.js Discord

Discord Forum

Functions cannot be passed directly to Client Components...

Answered
Spectacled bear posted this in #help-forum
Open in Discord
Avatar
Spectacled bearOP
Error message: Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server"

Let's say you have a server component like Page.tsx

export default async function Page() {
  return (
    <Suspense fallback={<p>Loading...</p>}>
        <ClientComponent
          someProp={() => console.log('Fails to render')}
        />
    </Suspense>
  )
}


Why does this cause problems?
Answered by Rafael Almeida
I can think about 3 solutions:
1. change itemRenderer to something like renderMethod so you can pass a string, and in the component you have all the possible rendering methods that you would want to use. this would only be good if you know about all the possible ways someone would want to use this component
2. create another client component that wraps ListComponent and receives only the data, then it renders the target component with the specific renderer:
'use client'

function DivListComponent({ data }) {
  return <ListComponent data={data} itemRenderer={item => <div>{item.name}</div>} />
}

3. change ListComponent to be a server component as well so you can pass a function as a prop normally
View full answer

14 Replies

Avatar
the server needs to be able to serialize the props you are passing to the client components because they are sent over the network, you can't serialize functions into JSON
Avatar
Spectacled bearOP
So there is no pattern for passing a function as a prop to a client component from a server component? It's hard to imagine that there wouldn't be a workaround for this
Avatar
what are you exactly trying to do? it just doesn't really make sense to pass a function to the client component
the only exception is in the case of server actions, which are a different thing that behave more like an extra endpoint that runs in the server
Avatar
Spectacled bearOP
I hope this doesn't come off as sarcastic, I'm not trying to be... but how does it not make sense exactly? It's a very common thing to pass a function to another component. For example, a render prop or a function that is used to sort an array of data. In my case, I am trying to pass a function that defines how the component should access an object and render children.
For example:

<ListComponent data={[{ name: 'Cat', id: 1 }, { name: 'Dog', id: 2 }]} itemRenderer={(item) => {
  return (
   <div>{item.name}</div>
  )
}} />
Avatar
this is a perfectly valid question. I can think of two reasons:
first, the server needs to serialize the props to send over the network, you can't serialize functions with JSON
and even if next.js managed to serialize the function, it would lead to weird code like this:
function Page() {
  const store = headers()
  return <Foo onClick={() => console.log(store.get('foo'))} />
}

when you click on Foo, the function you are passing as a prop will be invoked in the browser, it does not have access to the request headers so this code doesn't work at all. the point is that the server components have access to resources that can't run in the browser so passing a function that it could invoke doesn't make sense
Avatar
Spectacled bearOP
I understand
Avatar
as for your example, technically it could work but again, functions can't be serialized so there's no way to send this over the network to the browser. and it would stop working as soon as you change this div to something else like a server component
Avatar
Spectacled bearOP
But in that case the solution would be simply to call a client component from within another client component if it needs a function as a prop?
*render, not call
or nest lol
Avatar
I can think about 3 solutions:
1. change itemRenderer to something like renderMethod so you can pass a string, and in the component you have all the possible rendering methods that you would want to use. this would only be good if you know about all the possible ways someone would want to use this component
2. create another client component that wraps ListComponent and receives only the data, then it renders the target component with the specific renderer:
'use client'

function DivListComponent({ data }) {
  return <ListComponent data={data} itemRenderer={item => <div>{item.name}</div>} />
}

3. change ListComponent to be a server component as well so you can pass a function as a prop normally
Answer
Avatar
Spectacled bearOP
Yeah that's very helpful, I appreciate it man