Skip to content

Bug: Server functions work on event handler props in Server Components; is this intended? #36040

@slouloudis

Description

@slouloudis

Server functions work on event handler props in Server Components — is this intended?

This isn't a bug report - the behaviour described below works correctly. I'm raising this to ask whether it's intended/stable behaviour and whether it should be documented.

React version: 19

Steps To Reproduce

  1. Create a Server Component (no "use client" directive)
  2. Define a server function ("use server") inside it
  3. Pass the server function directly to an event handler prop on a native element (e.g., onClick, onMouseEnter)
  4. Trigger the event — it sends a POST request to the server and executes the function

Link to code example:

// This is a Server Component (no "use client" directive)
export default function Page() {
  async function handleClick() {
    "use server";
    console.log("click");
  }
 
  async function handleHover() {
    "use server";
    console.log("hovering...");
  }
 
  return (
    <div>
      <button onClick={handleClick}>Click me</button>
      <h2 onMouseEnter={handleHover}>Hover me</h2>
    </div>
  );
}

Note: Passing the event object to the server function (e.g., onClick={(e) => handleClick(e)}) errors out, presumably because the event cannot be serialized. The bare function reference works fine.

Tested across

Framework React Result
Next.js 16 19 ✅ Works
Next.js 15.5.9 19 ✅ Works
Next.js 14.2.35 18 ❌ Crashes: "Only plain objects, and a few built-ins, can be passed to Server Actions"
Waku 19 ✅ Works (independently confirmed)

The fact that this works on both Next.js and Waku suggests it's a React-level behaviour, not framework-specific.

The current behaviour

Server functions can be passed directly to any event handler prop on native elements inside Server Components. Both handlers fire POST requests to the server. This works on React 19 but crashes on React 18.

The expected behaviour

Based on the current documentation, I would expect this to require a Client Component wrapper. The documented patterns for server functions on native elements are:

  • As a form action prop
  • Passed as a prop to a Client Component, which then attaches it to an event handler

The Server Functions reference shows server functions used with onClick, but always through a Client Component wrapper that calls () => onClick(). The Server Components docs still state: "To add interactivity, compose them with Client Components."

What I think is happening

The React 19 serializer handles server function references on any event handler prop via isServerReference. In React 18, the serializer attempted to serialize the event object and failed. In React 19, the event appears to be discarded and the server function is invoked directly.

This seems like an emergent byproduct of the serializer doing its job rather than a deliberate feature - but it's a useful pattern and it would be helpful to know:

  1. Is this intended and stable behavior?
  2. If so, should the docs be updated to reflect it?
  3. If not, is it likely to change in future versions?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Status: UnconfirmedA potential issue that we haven't yet confirmed as a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions