Chapter 3: フォントと画像の最適化
Chapter 2 でスタイルの基礎を整えたので,ここからは表示速度と体験を意識した最適化に踏み込みます。カスタムフォントやヒーロー画像の扱いは,プロダクション品質を左右する大事な要素です。
この章で身につくこと
next/fontを使ってビルド時に最適化されたフォントを読み込むnext/imageで画面サイズに応じたレスポンシブ画像を提供する- フォントと画像がレイアウトシフトに与える影響を理解する
フォント最適化の背景
カスタムフォントを追加すると見た目はリッチになりますが,読み込みの遅延によって Cumulative Layout Shift(CLS) が発生し,コンテンツがガタつくことがあります。next/font を使うとビルド時にフォントファイルを取得し,他の静的アセットと同じ場所でホストしてくれるため,追加のネットワークリクエストが不要になり表示が安定します。

Step 1. プライマリフォントを定義する
/app/ui フォルダに fonts.ts を作成し,Google Fonts から Inter (というフォント名)を読み込みます。サブセットは 'latin' を指定します。
import { Inter } from 'next/font/google';
export const inter = Inter({ subsets: ['latin'] });
Step 2. レイアウトへ適用する
続いて /app/layout.tsx で inter を読み込み,<body> にクラスを付与します。Tailwind の antialiased を併用するとフォントレンダリングが滑らかになります。
import '@/app/ui/global.css';
import { inter } from '@/app/ui/fonts';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={`${inter.className} antialiased`}>{children}</body>
</html>
);
}
ブラウザの DevTools を開くと,<body> に Inter と Inter_Fallback が適用されているのが確認できます。
セカンダリフォントを追加する
特定の要素だけ別フォントを使いたいケースもよくあります。ここでは見出しに Lusitana を適用しましょう。
-
fonts.tsにLusitanaを追加し,必要なウェイト(400と700)を指定します。利用可能なウェイトは TypeScript の補完や Google Fonts のページ で確認できます。 -
/app/page.tsxでlusitanaを読み込み,本文の段落へ適用します。/app/page.tsximport AcmeLogo from '@/app/ui/acme-logo'; import { ArrowRightIcon } from '@heroicons/react/24/outline'; import Link from 'next/link'; import { lusitana } from '@/app/ui/fonts'; export default function Page() { return ( // ... <p className={`${lusitana.className} text-xl text-gray-800 md:text-3xl md:leading-normal`} > <strong>Welcome to Acme.</strong> This is the example for the{' '} <a href="https://nextjs.org/learn/" className="text-blue-500"> Next.js Learn Course </a> , brought to you by Vercel. </p> ); } -
これまではエラー防止のためコメントアウトされていた
<AcmeLogo />もLusitanaフォントを使うので,アンコメントして表示しておきましょう。
これでメインとサブの 2 種類のフォントが揃いました。
画像最適化の考え方
/public フォルダに置いた画像は <img> でも表示できますが,レスポンシブ対応や遅延読み込み,モダンフォーマットへの変換などを自前で行う必要があり,パフォーマンスチューニングの手間が増えます。next/image を使うとこれらを自動化でき,CLS の抑制にもつながります。
Step 3. <Image> コンポーネントを使う
/public には hero-desktop.png と hero-mobile.png が用意されています。まずはデスクトップ用の画像を追加しましょう。
import AcmeLogo from '@/app/ui/acme-logo';
import { ArrowRightIcon } from '@heroicons/react/24/outline';
import Link from 'next/link';
import { lusitana } from '@/app/ui/fonts';
import Image from 'next/image';
export default function Page() {
return (
{/* Add Hero Images Here */}
<div className="flex items-center justify-center p-6 md:w-3/5 md:px-28 md:py-12">
<Image
src="/hero-desktop.png"
width={1000}
height={760}
className="hidden md:block"
alt="Screenshots of the dashboard project showing desktop version"
/>
</div>
);
}
width と height は表示サイズではなく元画像の実寸です。縦横比を指定すると読み込み時のレイアウトシフトが抑えられます。
<Image> には以下のような最適化が標準で備わっています。
- ビューポートに入るまで画像を遅延読み込み
- デバイス幅に応じたサイズを自動生成
- 対応ブラウザには WebP / AVIF といった軽量フォーマットを配信
- 読み込み中のスペースを確保し CLS を回避
モバイル版のヒーロー画像を切り替える
続いてモバイル専用の画像を追加します。
- 画像:
/public/hero-mobile.png - 幅
560px、高さ620px - モバイルでは表示、デスクトップでは非表示(
block md:hidden)
import AcmeLogo from '@/app/ui/acme-logo';
import { ArrowRightIcon } from '@heroicons/react/24/outline';
import Link from 'next/link';
import { lusitana } from '@/app/ui/fonts';
import Image from 'next/image';
export default function Page() {
return (
// ...
<div className="flex items-center justify-center p-6 md:w-3/5 md:px-28 md:py-12">
{/* Add Hero Images Here */}
<Image
src="/hero-desktop.png"
width={1000}
height={760}
className="hidden md:block"
alt="Screenshots of the dashboard project showing desktop version"
/>
<Image
src="/hero-mobile.png"
width={560}
height={620}
className="block md:hidden"
alt="Screenshot of the dashboard project showing mobile version"
/>
</div>
);
}
DevTools のレスポンシブモードで表示を切り替え,正しく画像が変わっているか確認しましょう。
さらに学ぶ
- Next.js Font Optimization
- Next.js Image Optimization
- Web.dev: Core Web Vitals と CLS
- MDN: Web Fonts
- MDN: 画像でパフォーマンスを改善する
これでフォントと画像の最適化が完了しました。次章ではレイアウトとルーティングを深掘りしていきます。