Next.js 13 App Router中className不匹配及window未定义问题求助
Next.js 13 App Router 中 styled-components className 不匹配问题求助
控制台警告:
Warning: Prop `className` did not match. Server: "styled__HeadLink-sc-6589f63-0 fIOFTm" Client: "styled__HeadLink-sc-b76127ed-0 jzAIeh"
已尝试的解决方案
- 配置
.babelrc启用 styled-components SSR 支持:{ "env": { "development": { "presets": ["next/babel"], "plugins": [["babel-plugin-styled-components", { "ssr": true, "displayName": true, "preprocess": false }]] }, "production": { "plugins": [["babel-plugin-styled-components", { "ssr": true, "displayName": true, "preprocess": false }]], "presets": ["next/babel"] }, "test": { "presets": ["next/babel"] } }, "plugins": [["babel-plugin-styled-components", { "ssr": true, "displayName": true, "preprocess": false }]] } - 在
next.config.js中配置 styledComponents 编译选项:reactStrictMode: false, compiler: { styledComponents: true, }, - 添加客户端 window 存在性检查工具函数:
export const isClient = () => typeof window !== 'undefined'; - 使用
next/dynamic禁用组件 SSR:export default dynamic(() => Promise.resolve(VeryTroubleBlock), { ssr: false });
问题出现场景
问题仅在 Swiper 组件初始渲染时触发,相关组件代码如下:
const isTablet = useAdaptiveSize(Breakpoints.tablet); const isMobile = useAdaptiveSize(Breakpoints.mobile); return ( <div className="here_is_trouble"> {isTablet ? ( <Swiper modules={[Pagination, A11y]} pagination={{ clickable: true }} slidesPerView={isMobile ? 1 : 2} spaceBetween={24} > {managers.map((manager: TroubleBlockProps, index: number) => ( <SwiperSlide key={`${index}`}> <ManagerItems {...manager} /> </SwiperSlide> ))} </Swiper> ) : ( <> {managers.map((manager) => ( <ManagerItems key={manager.name} {...manager} /> ))} </> )} </div> ) export default dynamic(() => Promise.resolve(VeryTroubleBlock), { ssr: false });
自定义 useAdaptiveSize Hook 代码:
import { isClient } from '@/utils/client-check'; import { useState, useLayoutEffect } from 'react'; import { useWindowSize } from 'usehooks-ts'; export function useAdaptiveSize(size: number): boolean { const { width } = useWindowSize(); const [isTablet, setIsTablet] = useState(false); useLayoutEffect(() => { if (isClient()) setIsTablet(width < size); }, [width]); return isTablet; }
核心矛盾
- 该自定义 Hook 解决了应用中某页面的同类问题,但在包含上述 Swiper 的页面仍存在 className 不匹配警告。
- 若直接用
window.innerWidth < Breakpoints.tablet替代自定义 Hook,可消除 className 警告,但会触发服务器端window is not defined错误。
内容的提问来源于stack exchange,提问作者Takeshi 251
相关产品推荐
相关产品推荐

