字体优化

next/font 能自动优化字体加载:

  • 所有字体 自托管 成静态资源,浏览器不再向 Google 发请求。
  • 自动消除布局抖动(CLS)。
  • 字体只在用到的组件范围内生效,天然范围化。

Google Fonts

next/font/google 里直接导入字体并作为函数调用即可:

import { Geist } from 'next/font/google';

const geist = Geist({ subsets: ['latin'] });

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="zh-CN" className={geist.className}>
      <body>{children}</body>
    </html>
  );
}
  • 推荐使用 variable font,灵活且体积合理。
  • 普通字体则需要明确指定 weight
import { Roboto } from 'next/font/google';

const roboto = Roboto({ weight: '400', subsets: ['latin'] });

本地字体

.woff2 放到项目里(比如 app/fonts/public/fonts/),用 next/font/local

import localFont from 'next/font/local';

const myFont = localFont({ src: './my-font.woff2' });

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="zh-CN" className={myFont.className}>
      <body>{children}</body>
    </html>
  );
}

一组多权重/斜体?用数组:

const roboto = localFont({
  src: [
    { path: './Roboto-Regular.woff2', weight: '400', style: 'normal' },
    { path: './Roboto-Italic.woff2',  weight: '400', style: 'italic' },
    { path: './Roboto-Bold.woff2',    weight: '700', style: 'normal' },
    { path: './Roboto-BoldItalic.woff2', weight: '700', style: 'italic' },
  ],
});

作用域

字体是「用在哪里就作用于哪里」。把它写在根布局可全站生效,写在 app/blog/layout.tsx 就只影响博客区。这样你可以在不同路由用不同字体,互不干扰。

与 Tailwind 搭配

给字体起一个 CSS 变量,在 Tailwind 配置里映射成字体家族:

import { Geist } from 'next/font/google';

const geist = Geist({
  subsets: ['latin'],
  variable: '--font-geist',
});

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html className={geist.variable}>
      <body>{children}</body>
    </html>
  );
}
/* app/globals.css */
@theme {
  --font-sans: var(--font-geist);
}

这样 font-sans 工具类就会用上 Geist。

字體優化

next/font 會自動優化字體載入:

  • 所有字體 自行托管 為靜態資源,瀏覽器不再向 Google 發請求。
  • 自動消除版面抖動(CLS)。
  • 字體只於用到的元件範圍內生效,天然範圍化。

Google Fonts

next/font/google 直接 import 字體並呼叫即可:

import { Geist } from 'next/font/google';

const geist = Geist({ subsets: ['latin'] });

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="zh-HK" className={geist.className}>
      <body>{children}</body>
    </html>
  );
}
  • 建議採用 variable font,彈性且體積合理。
  • 一般字體則需明確指定 weight
import { Roboto } from 'next/font/google';

const roboto = Roboto({ weight: '400', subsets: ['latin'] });

本地字體

.woff2 放入專案(如 app/fonts/public/fonts/),使用 next/font/local

import localFont from 'next/font/local';

const myFont = localFont({ src: './my-font.woff2' });

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="zh-HK" className={myFont.className}>
      <body>{children}</body>
    </html>
  );
}

一組多重量/斜體?使用陣列:

const roboto = localFont({
  src: [
    { path: './Roboto-Regular.woff2', weight: '400', style: 'normal' },
    { path: './Roboto-Italic.woff2',  weight: '400', style: 'italic' },
    { path: './Roboto-Bold.woff2',    weight: '700', style: 'normal' },
    { path: './Roboto-BoldItalic.woff2', weight: '700', style: 'italic' },
  ],
});

作用域

字體「用於何處即作用於何處」。寫在根佈局可全站生效,寫在 app/blog/layout.tsx 僅影響網誌區。不同路由可使用不同字體且互不干擾。

與 Tailwind 搭配

為字體建立 CSS 變數,在 Tailwind 設定中映射為字體家族:

import { Geist } from 'next/font/google';

const geist = Geist({
  subsets: ['latin'],
  variable: '--font-geist',
});

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html className={geist.variable}>
      <body>{children}</body>
    </html>
  );
}
/* app/globals.css */
@theme {
  --font-sans: var(--font-geist);
}

如此 font-sans 工具類便會使用 Geist。

Font Optimization

next/font automatically optimizes font loading:

  • Self-hosts every font file, so the browser never hits Google.
  • Eliminates layout shift (CLS) from font swaps.
  • Scopes fonts to the component they're used in.

Google Fonts

Import a font from next/font/google and call it as a function:

import { Geist } from 'next/font/google';

const geist = Geist({ subsets: ['latin'] });

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" className={geist.className}>
      <body>{children}</body>
    </html>
  );
}
  • Prefer variable fonts — flexible and reasonably sized.
  • For non-variable fonts, specify a weight:
import { Roboto } from 'next/font/google';

const roboto = Roboto({ weight: '400', subsets: ['latin'] });

Local fonts

Drop .woff2 files into the project (e.g. app/fonts/ or public/fonts/) and use next/font/local:

import localFont from 'next/font/local';

const myFont = localFont({ src: './my-font.woff2' });

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en" className={myFont.className}>
      <body>{children}</body>
    </html>
  );
}

For multiple weights/styles in one family, pass an array:

const roboto = localFont({
  src: [
    { path: './Roboto-Regular.woff2', weight: '400', style: 'normal' },
    { path: './Roboto-Italic.woff2',  weight: '400', style: 'italic' },
    { path: './Roboto-Bold.woff2',    weight: '700', style: 'normal' },
    { path: './Roboto-BoldItalic.woff2', weight: '700', style: 'italic' },
  ],
});

Scope

Fonts are scoped to where they're used. Apply one at the root layout for the whole app, or at app/blog/layout.tsx to style only the blog area. Different routes can use different fonts without interfering.

Pairing with Tailwind

Expose the font as a CSS variable and map it to a Tailwind font family:

import { Geist } from 'next/font/google';

const geist = Geist({
  subsets: ['latin'],
  variable: '--font-geist',
});

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html className={geist.variable}>
      <body>{children}</body>
    </html>
  );
}
/* app/globals.css */
@theme {
  --font-sans: var(--font-geist);
}

Now the font-sans utility resolves to Geist.