
next.js 报错“event handlers cannot be passed to client component props”通常并非因缺失 `"use client"`,而是因服务端组件意外接收了客户端专属 prop(如 `onclick`),根本原因在于模块导出链未正确标记客户端边界。
在 Next.js 中,"use client" 是一个编译时指令,用于明确划分客户端组件边界,并触发 React Server Components(RSC)架构下的客户端 hydration 流程。但仅在组件实现文件(如 Button.js)顶部添加 "use client" 并不足够——该指令必须作用于被实际导入(import)的入口模块,否则 Next.js 构建系统无法识别该模块应作为客户端上下文执行,仍会将其视为服务端组件处理,导致事件处理器(如 onClick)被拒绝传递。
例如,你定义的组件看似合规:
// node_modules/your-package/Button/Button.js
"use client";
import * as React from "react";
export const Button = () => {
return ;
};但如果应用中直接通过 import { Button } from "your-package/Button/Button" 导入,Next.js 实际解析的是未声明 "use client" 的 包内中间导出文件(如 node_modules/your-package/Button/index.js),而该文件默认被视为服务端模块。此时,即使 Button.js 内部有 "use client",其导出内容仍被父级服务端模块“污染”,最终导致 onClick 被拦截并抛出错误。
✅ 正确做法是:在直接被项目 import 的入口文件(即导出路径的最上层 index 文件)中声明 "use client":
// node_modules/your-package/Button/index.js
"use client"; // ✅ 关键:此处必须声明
export { Button } from "./Button.js";同时确保项目中使用匹配的导入路径:
// ✅ 正确导入(触发 Button/index.js 的客户端上下文)
import { Button } from "your-package/Button";
// ❌ 避免直接导入深层实现文件(绕过客户端标记)
// import { Button } from "your-package/Button/Button"; // 不推荐此外,若使用 TypeScript,还需在 package.json 中为该子路径显式配置 "types" 或确保 .d.ts 声明文件同步遵循相同导出结构,避免类型检查与运行时行为不一致。
? 补充提示:
- Next.js 13.4+ 支持在 app/ 目录下通过 client 文件夹约定自动标记(如 app/components/client/Button.tsx),但第三方包不适用此约定,必须显式书写 "use client";
- 使用 create-react-app 或非 Next.js 打包器生成的代码(如问题中混淆后的 IIFE 模块)可能破坏 ESM 导出语义,建议始终以标准 ES Module 形式发布组件包;
- 可通过 next build --debug 或检查 .next/server/app/ 下生成的 SSR 组件产物,验证 "use client" 是否被正确注入到服务端 bundle 中。
归根结底,"use client" 不是组件内部的“开关”,而是模块边界的“签证”——它必须签发在调用链进入客户端逻辑的第一道门上。










