基于Material UI的TS组件库无法在Next.js应用中使用
React/MUI组件库在Next.js中触发React.createElement错误的解决方案
问题背景
我开发了一个基于React 18、MUI 5和Emotion的组件库,将核心依赖声明为peerDependencies以实现包体积优化和共享ThemeProvider:
"peerDependencies": { "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", "@mui/icons-material": "^5.10.15", "@mui/material": "^5.10.15", "react": "18.2.0", "react-dom": "18.2.0" }
Rollup配置中已将这些依赖设为外部依赖,避免打包进组件库:
external: [ 'react', 'react-dom', /@emotion\/.*/, /@mui\/.*/, ], output: [ { file: 'dist/index.cjs.min.js', format: 'cjs', sourcemap: true, plugins: [terser()], }, { file: 'dist/index.esm.min.js', format: 'esm', sourcemap: true, plugins: [terser()], }, ], plugins: [ resolve({ browser: true }), // 其他插件省略 typescript({ tsconfig: './tsconfig.json' }), ],
组件库已发布到npm,在Storybook和纯React应用中使用正常,但在Next.js 13 + Webpack 5项目中运行dev命令时,只要组件库引入MUI组件(如Paper)就会触发React.createElement错误,仅含纯div的测试组件能正常运行。尝试修改tsconfig、调整peerDependencies范围均无效。
核心原因
Next.js的Webpack 5模块解析逻辑与组件库的Rollup打包输出存在冲突,导致React/MUI/Emotion模块被重复加载:
- 组件库压缩后的ES模块可能干扰Next.js的tree-shaking和模块解析,使得应用和组件库加载了不同实例的React或MUI组件,触发React的"不同实例"错误(React要求全局仅能有一个实例)。
- MUI依赖的Emotion样式系统在Next.js的SSR/CSR混合环境下,因模块解析路径不一致,导致样式注入逻辑异常,进而引发
React.createElement调用失败。
解决步骤
1. 强制Next.js统一模块解析路径
在next.config.js中添加Webpack配置,强制让Next.js从项目根目录的node_modules加载核心依赖,避免组件库加载独立的依赖实例:
const path = require('path'); /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, webpack: (config, { isServer }) => { // 统一React、MUI、Emotion的模块解析路径 config.resolve.alias = { ...config.resolve.alias, 'react': path.resolve(__dirname, './node_modules/react'), 'react-dom': path.resolve(__dirname, './node_modules/react-dom'), '@mui/material': path.resolve(__dirname, './node_modules/@mui/material'), '@emotion/react': path.resolve(__dirname, './node_modules/@emotion/react'), '@emotion/styled': path.resolve(__dirname, './node_modules/@emotion/styled'), }; // 服务端与客户端保持一致的模块加载 if (!isServer) { config.externals = [...(config.externals || []), { react: 'React', 'react-dom': 'ReactDOM', }]; } return config; }, }; module.exports = nextConfig;
2. 调整组件库的Rollup输出配置
确保组件库的ES模块符合Next.js的优化要求:
- 移除ES模块的
terser压缩(让Next.js自行处理代码优化):
output: [ { file: 'dist/index.cjs.min.js', format: 'cjs', sourcemap: true, plugins: [terser()], }, { file: 'dist/index.esm.js', // 不压缩ES模块 format: 'esm', sourcemap: true, }, ],
- 在组件库的
package.json中明确指定模块入口:
{ "main": "dist/index.cjs.min.js", "module": "dist/index.esm.js", "types": "dist/index.d.ts" }
3. 验证并统一依赖版本
确保Next.js项目中的依赖版本与组件库的peerDependencies范围完全匹配,示例package.json依赖:
"dependencies": { "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", "@mui/icons-material": "^5.10.15", "@mui/material": "^5.10.15", "react": "18.2.0", "react-dom": "18.2.0", "next": "13.4.x" }
运行以下命令检查并清理重复依赖:
npm ls react # 检查是否存在多个React实例 npm dedupe # 合并重复依赖
4. 临时排查:禁用Strict Mode
若问题仍存在,可临时关闭React Strict Mode验证是否为严格模式兼容性问题:
const nextConfig = { reactStrictMode: false, // 其他配置... };
注:此为临时排查手段,建议排查完成后恢复严格模式。
内容的提问来源于stack exchange,提问作者Selena Mallet
相关产品推荐
相关产品推荐

