NextJS导入字体后出现Hydration错误的解决及规范用法咨询
Next.js 字体导入 Hydration 错误修复及规范用法
1. 修复Hydration错误
你的问题根源是在页面组件中直接自定义顶层<html>标签,导致服务端渲染与客户端 hydration 时的DOM结构不匹配。Next.js要求顶层的<html>标签由框架的根文档/布局统一管理,而非页面组件自行返回。
针对不同路由方案的修复:
Pages Router(pages目录结构)
- 创建/修改
pages/_document.js,在框架提供的<Html>组件上添加字体className:
import Document, { Html, Head, Main, NextScript } from 'next/document'; import { Lexend } from 'next/font/google'; const lexend = Lexend({ subsets: ['latin'] }); class MyDocument extends Document { render() { return ( <Html lang="en" className={lexend.className}> <Head /> <body> <Main /> <NextScript /> </body> </Html> ); } } export default MyDocument;
- 修改页面组件,移除自定义的
<html>标签,仅返回页面内容:
const SplashPage = () => { // ...业务逻辑 return ( <div> {/* 页面内容 */} </div> ); };
App Router(app目录结构)
- 修改
app/layout.tsx(根布局),在顶层<html>标签上添加字体className:
import { Lexend } from 'next/font/google'; const lexend = Lexend({ subsets: ['latin'] }); export default function RootLayout({ children }) { return ( <html lang="en" className={lexend.className}> <body>{children}</body> </html> ); }
- 页面组件(如
app/splash/page.tsx)仅返回内容部分,不要包含<html>标签。
2. Next.js 规范的字体导入方式
Next.js官方推荐使用next/font模块加载字体,分为全局使用和局部使用两种场景:
全局字体(全应用生效)
- 如上述修复方案,在Pages Router的
_document.js或App Router的根布局中导入字体,将className绑定到顶层<html>或<body>标签。 - 优势:自动优化字体加载(避免闪烁、预加载、无需额外CSS),全应用统一生效。
局部字体(仅特定组件/页面使用)
- 直接在目标组件/页面中导入字体,将className绑定到组件内部的元素,而非顶层标签:
import { Lexend } from 'next/font/google'; const lexend = Lexend({ subsets: ['latin'] }); const SplashPage = () => { return ( <div className={lexend.className}> {/* 仅该容器及其子元素使用Lexend字体 */} </div> ); };
自定义字体变量(适配CSS模块/全局样式)
- 可以为字体定义CSS变量,方便在样式文件中引用:
const lexend = Lexend({ subsets: ['latin'], variable: '--font-lexend', // 定义变量名 });
- 在根布局中注入变量:
<html className={lexend.variable}>
- 在CSS中使用:
.title { font-family: var(--font-lexend); }
内容的提问来源于stack exchange,提问作者usselman
相关产品推荐
相关产品推荐

