Next.js引入CKEditor时报ReferenceError: self is not defined如何解决
报错产生原因
ReferenceError: self is not defined 是典型的服务端渲染(SSR)环境兼容问题:
- Next.js 默认会在服务端(Node.js 环境)执行组件的导入、渲染逻辑,提前生成页面HTML
- CKEditor 5 是纯浏览器端依赖,包内代码在模块初始化阶段就会访问浏览器全局对象
self(即window.self的简写,指向浏览器全局作用域) - Node.js 环境中不存在
self这个全局变量,服务端执行到CKEditor的导入逻辑时就会直接抛出该错误
解决方法
核心思路是禁止CKEditor相关代码在服务端加载,只在客户端浏览器环境下初始化编辑器,以下是两种可直接落地的方案:
方案1:动态导入并关闭SSR(推荐,适配所有Next.js版本)
不要在组件顶部静态导入CKEditor,使用Next.js自带的next/dynamic方法导入依赖,手动关闭SSR:
// Next.js 13+ App Router 下使用必须在文件首行加客户端组件标记 'use client' import dynamic from 'next/dynamic' import { useState, useEffect } from 'react' // 动态导入CKEditor组件和编辑器构建包,全部关闭SSR const CKEditor = dynamic( () => import('@ckeditor/ckeditor5-react').then(mod => mod.CKEditor), { ssr: false } ) const ClassicEditor = dynamic( () => import('@ckeditor/ckeditor5-build-classic'), { ssr: false } ) export default function RichTextEditor() { const [mounted, setMounted] = useState(false) const [editorContent, setEditorContent] = useState('') // 确认组件在客户端挂载完成后再渲染编辑器 useEffect(() => { setMounted(true) }, []) if (!mounted) return <div>编辑器加载中...</div> return ( <CKEditor editor={ClassicEditor} data={editorContent} onChange={(event, editor) => { setEditorContent(editor.getData()) }} /> ) }
方案2:外层组件包裹动态导入(适合Pages Router项目)
如果已经写好了独立的编辑器组件,可以在引用编辑器的页面层做动态导入,不需要修改编辑器组件内部的导入逻辑:
// 页面文件中,例如 pages/article/publish.js import dynamic from 'next/dynamic' // 导入你封装好的编辑器组件,关闭SSR const RichTextEditor = dynamic(() => import('../../components/RichTextEditor'), { ssr: false, loading: () => <p>编辑器加载中...</p> }) export default function PublishPage() { return ( <div> <h1>发布文章</h1> <RichTextEditor /> </div> ) }
注意事项
- Next.js 13+ App Router 模式下,所有用到CKEditor的组件必须添加
'use client'指令,不能在服务端组件组件中直接引入CKEditor相关依赖 - 不要尝试通过polyfill给Node.js全局加
self变量模拟浏览器环境,会引发更多编辑器相关的DOM API报错
内容的提问来源于stack exchange,提问作者MD Jahid Hasan
相关产品推荐
相关产品推荐

