facebook/react · error · Error
374
374
Error message
Event handlers cannot be passed to Client Component props.%s If you need interactivity, consider converting part of this to a Client Component.
What it means
A function value reached a prop whose name matches /^on[A-Z]/ — React's convention for event handlers. Functions cannot be serialized from Server Components to Client Components, and event handlers specifically must run in the browser, so this case gets a dedicated message that also points at the fix (convert the interactive part to a Client Component).
Source
Thrown at packages/react-server/src/ReactFlightServer.js:4267
return serializeTemporaryReference(request, tempRef);
}
}
if (enableTaint) {
const tainted = TaintRegistryObjects.get(value);
if (tainted !== undefined) {
throwTaintViolation(tainted);
}
}
if (isOpaqueTemporaryReference(value)) {
throw new Error(
'Could not reference an opaque temporary reference. ' +
'This is likely due to misconfiguring the temporaryReferences options ' +
'on the server.',
);
} else if (/^on[A-Z]/.test(parentPropertyName)) {
throw new Error(
'Event handlers cannot be passed to Client Component props.' +
describeObjectForErrorMessage(parent, parentPropertyName) +
'\nIf you need interactivity, consider converting part of this to a Client Component.',
);
} else if (
__DEV__ &&
(jsxChildrenParents.has(parent) ||
(jsxPropsParents.has(parent) && parentPropertyName === 'children'))
) {
const componentName = value.displayName || value.name || 'Component';
throw new Error(
'Functions are not valid as a child of Client Components. This may happen if ' +
'you return ' +
componentName +
' instead of <' +
componentName +
' /> from render. ' +
'Or maybe you meant to call this function rather than return it.' +View on GitHub (pinned to eafeac097b)
Solutions
- Move the interactive piece into a 'use client' component that owns its own handler.
- Expose the behavior as a Server Action: mark the function 'use server' and pass that reference instead.
- Restructure props to data only (an action id or preset key the client component maps to a local handler).
Example fix
// before — Server Component
<SaveButton onClick={() => save(row.id)}>Save</SaveButton>
// after — the behavior is a server action
// actions.ts
'use server';
export async function save(id: string) { /* ... */ }
// SaveButton.tsx
'use client';
export function SaveButton({id}) {
return <button formAction={save.bind(null, id)}>Save</button>;
} Defensive patterns
Strategy: type-guard
Validate before calling
export function findFunctionProps(props: Record<string, unknown>): string[] {
return Object.entries(props)
.filter(([, v]) => typeof v === 'function')
.map(([k]) => k);
}
// DEV guard before returning JSX from a server component:
const bad = findFunctionProps(props);
if (bad.length) throw new Error('non-serializable props: ' + bad.join(', ')); Type guard
export function isEventHandlerProp(name: string, value: unknown): boolean {
return /^on[A-Z]/.test(name) && typeof value === 'function';
} Prevention
- Mark interactive components 'use client'; keep server files passing data only.
- Use 'use server' actions for cross-boundary behavior, never closures.
- Lint against function props on components imported into server files.
When it happens
Trigger: <ClientButton onClick={() => save(id)} /> rendered from a Server Component; passing onChange/onSubmit handlers down through a props object; cloneElement injecting onXxx props inside server-rendered layout code.
Common situations: Converting an existing client page to RSC and letting a handler closure cross the boundary; shared UI kits whose handler props are used from server trees; copy-pasted interactive snippets into server files.
Related errors
AI-assisted analysis of facebook/react@eafeac097b (2026-08-21).
Data as JSON: /api/errors/93fc0e86b30add8f.
Report an issue: GitHub.