afaqrashid.com  ·  portfolio · 2026 ed.

"Event handlers cannot be passed to Client Component props" in Next.js: Fixes Tested

In the App Router, components are Server Components unless a file says "use client". A Server Component runs on the server and sends its output to the browser as data, so everything it passes to a Client Component has to survive that trip. A function can't: it's code living in the server's memory. That's what all three of these errors are about.

I reproduced each one on Next.js 16.4.4 with next build, then built the fixes and clicked them in a browser.

Which message you get

what the Server Component passed                  error
onClick on a <button>                             Event handlers cannot be passed to Client Component props.
a function prop named onPick to a client component Event handlers cannot be passed to Client Component props.
a function prop named format to a client component Functions cannot be passed directly to Client Components unless you explicitly expose it by marking it with "use server".
an instance of a class                            Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.

The first two are the same problem. The message depends only on the prop name: any function prop starting with "on" gets the event-handler wording, anything else gets the "Functions cannot be passed directly" wording. The error also prints the props with a marker under the bad one, such as {onClick: function onClick, children: ...}, which tells you which prop to look at.

On a static page it fails the build with "Error occurred prerendering page". On a dynamic page the build passes and the request returns a 500 with the same message in the server log, so you may only see it after deploying.

Fix 1: move the interactive part into a Client Component

// components/LikeButton.tsx
"use client";

export default function LikeButton({ id }: { id: string }) {
  return <button onClick={() => like(id)}>Like</button>;
}

// app/post/[id]/page.tsx (still a Server Component)
import LikeButton from "@/components/LikeButton";

export default async function Page({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params;
  return <LikeButton id={id} />;
}

The page passes a string, which is fine, and the onClick lives inside the client file. Clicking the button ran the handler. Keep the client part as small as you can: the button, not the whole page. Don't fix it by adding "use client" to the page itself, because then you lose server-side data fetching for everything in it.

Fix 2: pass a Server Action instead of a function

import Picker from "@/components/Picker"; // a client component

async function pick(value: string) {
  "use server";
  await savePick(value);
}

export default function Page() {
  return <Picker onPick={pick} />;
}

A function marked "use server" is allowed, because React sends a reference to it, not the code. Clicking in the browser called it on the server (it logged the value). Use this when the function's job is server work, such as saving. It won't work for something like a formatter the client needs to run on every render; move that function into the client file instead.

Fix 3: turn class instances into plain objects

Error: Only plain objects, and a few built-ins, can be passed to Client Components from Server Components. Classes or null prototypes are not supported.
  {m: {amount: 5}}
      ^^^^^^^^^^^

This comes from passing an instance of a class: your own, or one from a library, such as a database driver's ID or decimal type. Pass only the fields the client needs, as plain values: { amount: money.amount }, or id.toString() for an ID.

Many built-ins do go through, so you don't need to convert them. In my test a Date arrived as a real Date, a Map and a Set arrived as a Map and a Set, a BigInt as a BigInt, null as null, and nested objects and arrays were fine. A key set to undefined was dropped from the object.

If it shows up in a layout or a library

  • The error marker shows the props of the component that received the function. Search your Server Components for that prop name.
  • A UI library without "use client" at the top of its files can't take onClick from a Server Component. Wrap the component you use in your own file that starts with "use client" and re-export it.
  • Passing render props or children as functions (children={(x) => ...}) from a Server Component hits the same error. Build that part in a Client Component.

All results come from one Next.js 16.4.4 project built with next build and run with next start on Node 26, with clicks tested in Chrome.

Related

Sources