Next.js 13(App目录)className不匹配警告及样式加载异常求助
修复Next.js 13 App目录中styled-components + MUI的className不匹配与样式闪烁问题
问题根源
你同时使用了styled-components和MUI基于Emotion的styled两个独立样式系统,但当前仅配置了styled-components的服务端样式注入,缺少MUI(Emotion)的服务端样式处理,导致:
- 服务端未渲染MUI自定义样式,客户端hydration时补全样式出现闪烁
- 服务端与客户端生成的className不一致,触发「Prop
classNamedid not match」警告
具体修复步骤
1. 添加MUI Emotion的服务端样式注册表
创建EmotionRegistry.tsx组件,处理MUI样式的服务端注入:
"use client"; import React, { useState } from "react"; import { useServerInsertedHTML } from "next/navigation"; import { CacheProvider } from "@emotion/react"; import createCache from "@emotion/cache"; export default function EmotionRegistry({ children }: { children: React.ReactNode }) { const [cache] = useState(() => { const cache = createCache({ key: "mui-style" }); cache.compat = true; return cache; }); useServerInsertedHTML(() => { return ( <style data-emotion={`${cache.key} ${Object.keys(cache.inserted).join(" ")}`} dangerouslySetInnerHTML={{ __html: Object.values(cache.inserted).join(" "), }} /> ); }); return <CacheProvider value={cache}>{children}</CacheProvider>; }
2. 修复StyledComponentsRegistry的客户端逻辑
当前组件在客户端直接返回children,导致客户端与服务端className生成逻辑不一致,修改为:
"use client"; import React, { useState } from "react"; import { useServerInsertedHTML } from "next/navigation"; import { ServerStyleSheet, StyleSheetManager } from "styled-components"; export default function StyledComponentsRegistry({ children, }: { children: React.ReactNode; }) { const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet()); useServerInsertedHTML(() => { const styles = styledComponentsStyleSheet.getStyleElement(); styledComponentsStyleSheet.instance.clearTag(); return <>{styles}</>; }); // 客户端也用StyleSheetManager包裹,保证className生成规则一致 return ( <StyleSheetManager sheet={styledComponentsStyleSheet.instance}> {children} </StyleSheetManager> ); }
3. 调整根Layout的组件嵌套顺序
将两个样式注册表按正确顺序嵌套,确保MUI的主题和样式能正常生效:
"use client"; import Navbar from "../components/navbar/Navbar"; import { ThemeProvider } from "@mui/material"; import theme from "../theme"; import "../globals.css"; import { Inter } from "next/font/google"; import StyledComponentsRegistry from "../components/StyledComponentsRegistry"; import EmotionRegistry from "../components/EmotionRegistry"; // 导入新组件 const inter = Inter({ subsets: ["latin"] }); export default function Layout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <body className={inter.className}> {/* 先包裹Emotion处理MUI样式,再包裹styled-components */} <EmotionRegistry> <StyledComponentsRegistry> <ThemeProvider theme={theme}>{children}</ThemeProvider> </StyledComponentsRegistry> </EmotionRegistry> </body> </html> ); }
4. 可选:统一样式系统(推荐)
如果不需要同时维护两个样式系统,建议统一用styled-components创建MUI自定义样式,避免冲突:
// styles.ts 改用styled-components的styled import styled from "styled-components"; export const PromoStyled = styled.div` display: flex; flex-direction: column; align-items: center; justify-content: center; .tagline { max-width: 600px; font-size: 40px; font-weight: 900; opacity: 90%; color: ${(props) => props.theme.palette.primary.main}; text-shadow: #e0ffce -1px 0px, #e0ffce 0px 3px, #e0ffce 3px 0px, #e0ffce 0px -1px, #e0ffce 5px 7px 0px; } .teaser-image { margin-top: 20px; } `;
内容的提问来源于stack exchange,提问作者Umair
相关产品推荐
相关产品推荐

