You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

在Next.js Pages Router中结合Tailwind v4的CSS优先配置正确定义next/font的方法

在Next.js Pages Router中结合Tailwind v4的CSS优先配置正确定义next/font的方法

我之前在Pages Router下用Tailwind v4的CSS优先模式配置next/font时,也踩过一模一样的「--font-inter is not defined」坑,完全懂这种代码逻辑看起来没问题,但控制台报错的烦躁😅。核心问题就是加载顺序搞反了:Tailwind的主题变量在globals.css里先初始化了,但next/font的CSS变量要等到_app.tsx组件渲染才会挂载到DOM上,这时候Tailwind早就完成初始化了,自然找不到变量。

而且官方文档确实有bug,给Pages Router的例子混用了App Router的写法,浪费了我不少时间。下面是我实测可行的解决方法,核心就是让字体变量在Tailwind初始化之前就出现在页面中:

解决思路

要打破这个加载顺序的死循环,我们不能等到_app.tsx渲染组件时才挂载字体变量,而是要把next/font的CSS变量提前到页面的<head>里注入。虽然直接在_document.tsx里用next/font的组件会报错,但我们可以用next/font提供的getVariableCss()方法手动提取变量CSS,然后插入到<head>的内联样式中。

具体步骤

1. 统一管理字体实例(可选但推荐)

先抽离一个单独的文件来定义所有字体实例,避免在多个文件重复写,维护起来更方便,比如创建lib/fonts.ts

// lib/fonts.ts
import { Inter, Inter_Tight, Roboto_Mono } from "next/font/google";

export const inter = Inter({
  variable: "--font-inter",
  subsets: ["latin"],
});

export const interTight = Inter_Tight({
  variable: "--font-inter-tight",
  subsets: ["latin"],
});

export const robotoMono = Roboto_Mono({
  variable: "--font-roboto-mono",
  subsets: ["latin"],
  weight: ["400", "700"],
});

2. 修改_document.tsx注入字体变量CSS

pages/_document.tsx中,导入我们的字体实例,用getVariableCss()获取变量的CSS代码,然后作为内联样式插入到<head>里。这样页面加载时,这些字体变量会先于globals.css加载:

// pages/_document.tsx
import Document, { Html, Head, Main, NextScript } from "next/document";
import { inter, interTight, robotoMono } from "@/lib/fonts";

export default class MyDocument extends Document {
  render() {
    return (
      <Html>
        <Head>
          {/* 注入字体变量的CSS,确保它在globals.css之前加载 */}
          <style
            dangerouslySetInnerHTML={{
              __html: `
                ${inter.getVariableCss()}
                ${interTight.getVariableCss()}
                ${robotoMono.getVariableCss()}
              `,
            }}
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

3. 简化_app.tsx的代码

现在字体变量已经在页面初始HTML里存在了,_app.tsx里就不需要再给根容器加那些variable类了,只需要导入全局样式即可:

// pages/_app.tsx
import type { AppProps } from "next/app";
import "@/styles/globals.css";

export default function App({ Component, pageProps }: AppProps) {
  return <Component {...pageProps} />;
}

4. 保持globals.css的主题配置不变

你原来写的globals.css主题配置是完全正确的,现在字体变量已经提前加载,不会再出现未定义的错误:

/* src/styles/globals.css */
@import "tailwindcss";

@theme inline {
  --font-sans: var(--font-inter);
  --font-narrow: var(--font-inter-tight);
  --font-mono: var(--font-roboto-mono);
}

为什么这个方法有效?

通过在_document.tsx的<head>中插入字体变量的CSS,这些变量会在页面的初始HTML中就存在,当浏览器加载globals.css并执行Tailwind的@theme inline时,就能直接找到--font-inter这些变量,完美避开了加载顺序的问题。

我就是用这个方法解决了自己遇到的同样问题,亲测在Pages Router + Tailwind v4的CSS优先模式下完全可行,也避开了官方文档的错误示例。

火山引擎 最新活动