😺

Next.js(App Router)のnext/fontでGoogleフォントをセルフホスティング

2023/06/29に公開

はじめに

  • Next.js の Font Optimization(フォント最適化)について紹介します。
  • 実際のプロジェクトを作成し、Google フォント をでフォント最適化します。
  • TailwindCSS で Google フォントを利用する方法も紹介します。

今回の作業結果は以下にあります。
https://github.com/hayato94087/nextjs-next-font-sample

フォント最適化

next/fontを利用することで、サイトで使用するフォントが自動的に Next.js サーバでセルフホスティングされます。これにより、これまで追加でフォントをダウンロードするために発生していた通信が削減され、ページの読み込み速度が向上します。Google フォントの場合は、ブラウザから Google への通信が発生しなくなり、通信量が削減されます。

公式サイト
https://nextjs.org/docs/app/building-your-application/optimizing/fonts

環境を構築

プロジェクトを新規に作成します。

$ pnpm create next-app@latest nextjs-next-font-sample --typescript --eslint --import-alias "@/*" --src-dir --use-pnpm --tailwind --app
$ cd nextjs-next-font-sample

不要な設定を削除し、クリーンアップします。

クリーンアップ

以下の通り上書きします。

src/app/globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
src/app/page.tsx
export default function Home() {
  return (
    <main className="">
      テストページ
    </main>
  )
}
src/pages/layout.tsx
import "./globals.css";

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

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

ローカルサーバで動作確認します。

$ pnpm dev

$ pnpm build
$ git add .
$ git commit -m "delete unnecessary files"

next/font を単一ページで利用

next/font を実際に使ってみます。ここでは、特定ページのみでフォントを適用する方法を紹介します。

以下のフォントを利用します。

  • next/font/google から Rampart_Oneimport します。
  • weight400 を指定します。
  • subsetslatin を指定します。(japanese を指定するとタイプエラーになります。)
src/app/page.tsx
import { Rampart_One } from "next/font/google";

const RampartOneFont = Rampart_One({
  weight: "400",
  subsets: ["latin"],
});

export default function Home() {
  return (
    <main className="">
      <div className={RampartOneFont.className}>人類社会のすべての構成員の固有の尊厳と平等で譲ることのできない権利とを承認することは</div>
    </main>
  )
}

以下の通りフォントが適応されました。

コミット
$ pnpm build
$ git add .
$ git commit -m "add google font to single page"

next/font を複数ページで利用

ページの全体あるいは特定のページの塊にフォントを紹介します。

以下のように layout.tsx を修正することで、layout.tsx が適応される複数ページに対してフォントを適用できます。

src/app/layout.tsx
import "./globals.css";
+import { Hachi_Maru_Pop } from "next/font/google";

+const HachiMaruPopFont = Hachi_Maru_Pop({ weight: "400", subsets: ["latin"] });

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="ja">
-      <body className="">{children}</body>
+      <body className={HachiMaruPopFont.className}>{children}</body>
    </html>
  );
}

1 行を追加して、layout.tsx で指定されたフォント、page.tsx の単体ページで指定されたフォントの違いが分かるようにします。

src/app/page.tsx
import { Rampart_One } from "next/font/google";

const RampartOneFont = Rampart_One({
  weight: "400",
  subsets: ["latin"],
});

export default function Home() {
  return (
    <main className="text-4xl">
+     <div>人類社会のすべての構成員の固有の尊厳と平等で譲ることのできない権利とを承認することは</div>
      <div className={RampartOneFont.className}>人類社会のすべての構成員の固有の尊厳と平等で譲ることのできない権利とを承認することは</div>
    </main>
  )
}

正しく適応されていることが確認できます。

コミット
$ pnpm build
$ git add .
$ git commit -m "add google font to layout"

Tailwindを利用する場合

next/font を TailwindCSS と組み合わせることで、TailwindCSS から Google フォントを指定できます。ここでは TailwindCSS と next/font を組み合わせ利用する方法について紹介します。

page.tsx を以下のように修正し、Google フォントを削除します。

src/app/page.tsx
-import { Rampart_One } from "next/font/google";

-const RampartOneFont = Rampart_One({
-  weight: "400",
-  subsets: ["latin"],
-});

export default function Home() {
  return (
    <main className="text-4xl">
      <div>人類社会のすべての構成員の固有の尊厳と平等で譲ることのできない権利とを承認することは</div>
-      <div className={RampartOneFont.className}>人類社会のすべての構成員の固有の尊厳と平等で譲ることのできない権利とを承認することは</div>
+      <div className="">人類社会のすべての構成員の固有の尊厳と平等で譲ることのできない権利とを承認することは</div>
    </main>
  )
}
  • リファクタリングとして、DotGothic16Fontlayout.tsx に移動させます。
  • CSS Variable を定義します。
  • CSS Variable を<body>に追加します。
src/app/layout.tsx
import "./globals.css";
-import { Hachi_Maru_Pop } from "next/font/google";
+import { Hachi_Maru_Pop, Rampart_One } from "next/font/google";

-const HachiMaruPopFont = Hachi_Maru_Pop({ weight: "400", subsets: ["latin"] });
+const HachiMaruPopFont = Hachi_Maru_Pop({
+  weight: "400",
+  subsets: ["latin"],
+  variable: "--font-HachiMaruPop",
+});
+const RampartOneFont = Rampart_One({
+  weight: "400",
+  subsets: ["latin"],
+  variable: "--font-RampartOne",
+});

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="ja">
-      <body className={`${HachiMaruPopFont.variable} ${RampartOneFont.variable}`}>
+      <body className={`${HachiMaruPopFont.variable} ${RampartOneFont.variable}`}>
        {children}
      </body>
    </html>
  );
}

TailwindCSS でフォントを呼び出せるように、TailwindCSS の設定で、フォントを拡張します。

tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      backgroundImage: {
        "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
        "gradient-conic":
          "conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
      },
+      fontFamily: {
+        HachiMaruPop: ["var(--font-HachiMaruPop)"],
+        RampartOne: ["var(--font-RampartOne)"],
+      },
    },
  },
  plugins: [],
};

layout.tsx はさらに修正します。ポイントは、className にて font-MPlusRounded1cFont でフォントを指定しているところです。

src/app/layout.tsx
import "./globals.css";
import { Hachi_Maru_Pop, Rampart_One } from "next/font/google";

const HachiMaruPopFont = Hachi_Maru_Pop({
  weight: "400",
  subsets: ["latin"],
  variable: "--font-HachiMaruPop",
});
const RampartOneFont = Rampart_One({
  weight: "400",
  subsets: ["latin"],
  variable: "--font-RampartOne",
});

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="ja">
-      <body className={`${HachiMaruPopFont.variable} ${RampartOneFont.variable}`}>
+      <body className={`${HachiMaruPopFont.variable} ${RampartOneFont.variable} font-HachiMaruPop`}>
        {children}
      </body>
    </html>
  );
}

正しく適応されていることが確認できます。

コミット
$ pnpm build
$ git add .
$ git commit -m "integrate with TailwindCSS"

利用するフォントについて

Google フォントの利用において、next/font で最高のパフォーマンスを出すためには、Variable fontsの使用が公式として推奨されています。

フォントの再利用について

特定のフォントを複数箇所で再利用する場合は、const で定義し export することで、再利用が可能になります。フォントを export で共有しない場合、同一のフォントのインスタンスがサーバ上で複数作成されることになります。(公式ページを参照)

まとめ

  • Next.js の Font Optimization(フォント最適化)について紹介しました。
  • 実際のプロジェクトを作成し、Google フォント をでフォント最適化しました。
  • TailwindCSS で Google フォントを利用する方法も紹介しました。

今回の作業結果は以下にあります。
https://github.com/hayato94087/nextjs-next-font-sample

Discussion