13.

Next.js の next/image と next/font の使い方

編集

この記事の要点

  • next/image は、サイズの最適化・レイアウトずれの防止・遅延読み込みをまとめて引き受ける。
  • import した画像なら widthheight は自動。外部 URL の画像では自分で指定する。
  • 外部の画像を使うには images.remotePatterns での許可が必要。ここはできるだけ具体的に書く。
  • バージョン 16 で画像の既定値がいくつか変わった。quality75 のみ、キャッシュ最小時間は 4 時間。
  • next/font はフォントを自前で配信する。Google Fonts でもブラウザから外部への通信は発生しない。

表示速度の指標で真っ先に問題になるのが画像とフォントです。どちらも「大きなファイルが、必要になる前に、間違ったサイズで読み込まれる」ことで遅くなります。Next.js はこの 2 つに専用の仕組みを持っており、使うだけで大半の問題が消えます。

1next/image が引き受けること

課題next/image の対応
大きすぎる画像を配信してしまう端末に合ったサイズを生成し、WebP などの形式で返す
読み込み完了時に画面が飛ぶ縦横比を先に確保してレイアウトのずれを防ぐ
画面外の画像まで読み込むブラウザ標準の遅延読み込みを使う
外部サーバーの画像を最適化できない許可した配信元の画像を実行時に最適化する

2ローカル画像

プロジェクト直下の public に置いたファイルは、/ 始まりのパスで参照できます。この場合は widthheight を自分で書きます。

import Image from 'next/image'

export default function Page() {
  return (
    <Image
      src="/profile.png"
      alt="著者の写真"
      width={500}
      height={500}
    />
  )
}

ファイルを import すると、ビルド時に実寸が分かるためサイズ指定が不要になります。ぼかしのプレースホルダ用データも自動で用意されます。

import Image from 'next/image'
import ProfileImage from './profile.png'

export default function Page() {
  return (
    <Image
      src={ProfileImage}
      alt="著者の写真"
      placeholder="blur"   // 読み込み中はぼかし表示
    />
  )
}

ファイル名が実行時にしか決まらない場合は、サーバーコンポーネントの中で動的 import() を使えば同じ利点を得られます。ただしパスには固定の前置きが必要で、その配下のファイルはすべてバンドルに含まれる点に注意してください。

3外部サーバーの画像

URL を直接渡す場合、ビルド時にはファイルを読めないため widthheight が必須です。親要素いっぱいに広げたい場合は fill を使います。

加えて、配信元を明示的に許可する必要があります。任意の URL を最適化できてしまうと、自分のサーバーが第三者の画像変換に使われてしまうためです。

// next.config.ts
import type { NextConfig } from 'next'

const config: NextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 's3.amazonaws.com',
        port: '',
        pathname: '/my-bucket/**',
        search: '',
      },
    ],
  },
}

export default config

ホスト名だけで広く開けるのではなく、pathname まで絞るのが推奨です。なお、以前使われていた images.domains は非推奨になりました。remotePatterns に置き換えてください。

4バージョン 16 で変わった既定値

画像まわりは既定値の変更が多く、古い記事のまま設定すると挙動が変わります。

設定以前バージョン 16影響
minimumCacheTTL60 秒4 時間再生成の頻度が下がり、負荷とコストが減る
qualitiesすべて許可[75] のみ他の値を渡すと最も近い値に丸められる
imageSizes16 を含む16 を除外srcset が短くなる
maximumRedirects無制限3 回リダイレクトの多い配信元で失敗しうる
ローカル IP許可既定で拒否社内ネットワーク配信では明示的な許可が必要
クエリ付きローカル画像そのまま可localPatterns が必要/img.png?v=1 形式が 400 になる

とくに quality を 90 などで指定していた場合、警告なく 75 に丸められます。意図した画質が必要なら、設定側で許可する値を列挙してください。

// next.config.ts
const config = {
  images: {
    qualities: [50, 75, 100],
  },
}

5フォントの自前配信

next/font は、指定したフォントをビルド時に取り込み、自サイトから配信します。ブラウザから Google のサーバーへリクエストが飛ばないため、表示の安定とプライバシーの両面で有利です。

// app/layout.tsx
import { Geist } from 'next/font/google'

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

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

フォントは使ったコンポーネントの範囲に効きます。サイト全体に適用したい場合はルートレイアウトで指定します。

可変フォント(バリアブルフォント)が使える場合はそちらが推奨されます。太さごとにファイルを持たずに済むためです。可変フォントでない場合は weight の指定が必要になります。

import { Roboto } from 'next/font/google'

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

6ローカルのフォントファイル

手元の woff2 を使う場合は next/font/local です。パスは、その関数を呼び出しているファイルからの相対で解決されます。

import localFont from 'next/font/local'

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

同じ書体で太さや斜体を使い分けるなら、配列で渡します。

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' },
  ],
})
外部の CSS からフォントを読む場合 HTML を取得 外部 CSS を取得 フォント本体を取得 往復が多い next/font を使う場合 HTML を取得 同じドメインからフォントを取得 外部への接続なし・ずれなし
外部フォントは「CSS を取ってから本体を取る」二段構えになる。next/font は自前配信にすることでこの往復とレイアウトのずれを解消する。

7つまずきやすいところ

  • 外部画像が表示されないremotePatterns に登録されていません。エラーメッセージにホスト名が出ます。
  • width / height が必須と怒られる:外部 URL では自動判定できません。fill を使う場合は親要素に位置指定が必要です。
  • 画質が変わった:バージョン 16 の qualities 既定値によって丸められています。
  • alt を空にする:装飾目的なら空文字が正解ですが、内容を伝える画像には必ず説明を入れます。
  • 日本語フォントを丸ごと読み込む:欧文と違い、日本語のフォントはファイルが大きくなります。本文に使う場合はサブセット化や、システムフォントとの併用を検討してください。

アプリの中身が仕上がったら、最後は本番環境へ出す工程です。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 開発環境の構築とプロジェクトの初期構成
  2. App Router のルーティングとファイル規約
  3. レイアウトと特殊ファイルの使い分け
  4. サーバーコンポーネントとクライアントコンポーネント
  5. サーバー側でのデータ取得と並列化
  6. ストリーミングで体感速度を上げる
  7. キャッシュの仕組みと use cache
  8. 再検証の使い分け
  9. フォーム送信とデータ更新の実装
  10. エラーハンドリングとエラー境界
  11. Route Handlers で API を作る
  12. メタデータ API で SEO を整える
  13. 画像とフォントの最適化
  14. 本番ビルドとセルフホスト
  15. バージョン16 への移行と注意点