Next.js 13集成lottie-react出现ReferenceError:SSR兼容性问题?
问题描述
在Next.js 13项目中安装lottie-react后,项目编译成功,但运行时抛出ReferenceError,提示document is not defined,错误日志如下:
✓ Compiled in 104ms (1559 modules) ⨯ ReferenceError: document is not defined at createTag (/Users/thomas/starknetid/app.starknet.id/node_modules/lottie-web/build/player/lottie.js:30:5) at /Users/thomas/starknetid/app.starknet.id/node_modules/lottie-web/build/player/lottie.js:1316:20 at /Users/thomas/starknetid/app.starknet.id/node_modules/lottie-web/build/player/lottie.js:1323:6 at /Users/thomas/starknetid/app.starknet.id/node_modules/lottie-web/build/player/lottie.js:1540:4 at /Users/thomas/starknetid/app.starknet.id/node_modules/lottie-web/build/player/lottie.js:2:83 at Object.<anonymous> (/Users/thomas/starknetid/app.starknet.id/node_modules/lottie-web/build/player/lottie.js:5:3) at Module._compile (node:internal/modules/cjs/loader:1376:14) at Module._extensions..js (node:internal/modules/cjs/loader:1435:10) at Module.load (node:internal/modules/cjs/loader:1207:32) at Module._load (node:internal/modules/cjs/loader:1023:12) at Module.require (node:internal/modules/cjs/loader:1235:19) at mod.require (/Users/thomas/starknetid/app.starknet.id/node_modules/next/dist/server/require-hook.js:64:28) at require (node:internal/modules/helpers:176:18) at Object.<anonymous> (/Users/thomas/starknetid/app.starknet.id/node_modules/lottie-react/build/index.js:5:14) at Module._compile (node:internal/modules/cjs/loader:1376:14) { page: '/register/testingdomain.stark' }
推测问题源于lottie-react的SSR兼容性——客户端环境中document已定义,但SSR环境无法访问该对象。现需了解:在SSR场景下,lottie-react与Next.js集成的解决方案或最佳实践;是否需要在每个使用lottie的组件中判断SSR状态?
解决方案与最佳实践
1. 动态导入并禁用SSR(推荐)
利用Next.js的动态导入功能,将lottie-react组件标记为仅在客户端渲染,无需手动判断SSR状态。示例代码:
import dynamic from 'next/dynamic'; const Lottie = dynamic(() => import('lottie-react'), { ssr: false, loading: () => <div>加载中...</div> // 可选:添加加载占位符 }); // 使用组件 export default function MyLottieComponent() { return <Lottie animationData={yourAnimationData} />; }
这种方式会自动跳过SSR阶段,仅在客户端加载组件,从根源避免document未定义的问题。
2. 组件内判断客户端环境
如果需要更细粒度的控制,可以在组件内部通过typeof window !== 'undefined'判断是否处于客户端环境,再渲染lottie内容:
import Lottie from 'lottie-react'; export default function MyLottieComponent() { if (typeof window === 'undefined') { return <div>占位内容</div>; // SSR阶段返回占位符 } return <Lottie animationData={yourAnimationData} />; }
这种方法适合需要在SSR阶段展示自定义占位内容的场景,但需要在每个使用lottie的组件中重复判断逻辑。
3. 封装通用Lottie组件
为了避免重复编写判断逻辑,可以封装一个通用的客户端Lottie组件,在项目中统一使用:
// components/ClientLottie.js import dynamic from 'next/dynamic'; const Lottie = dynamic(() => import('lottie-react'), { ssr: false, loading: () => <div>加载中...</div> }); export default Lottie;
之后在项目中直接导入这个封装好的组件即可,无需每次处理SSR逻辑。
4. 使用lottie-web的纯客户端初始化
如果不想用lottie-react封装,也可以直接使用lottie-web,并在useEffect中初始化(仅在客户端执行):
import { useEffect, useRef } from 'react'; import lottie from 'lottie-web'; export default function MyLottieComponent() { const containerRef = useRef(null); useEffect(() => { if (containerRef.current) { lottie.loadAnimation({ container: containerRef.current, animationData: yourAnimationData, loop: true }); } }, []); return <div ref={containerRef} />; }
useEffect的回调函数仅在客户端渲染完成后执行,自然避开SSR环境。
总结
- 优先使用动态导入禁用SSR的方案,简洁高效,无需重复逻辑;
- 若需自定义SSR占位内容,可在组件内判断客户端环境;
- 封装通用组件能进一步简化项目中的使用流程;
- 直接使用
lottie-web结合useEffect也是可靠的替代方案。
内容的提问来源于stack exchange,提问作者Th0rgal
相关产品推荐
相关产品推荐

