パフォーマンス

next/imageで画像を最適化するときにつまずいたこと

読了 6分
パフォーマンス

next/imageで画像を最適化するときにつまずいたこと

Next.jsの next/image を使い始めたとき、素の img タグと作法が違って fillsizes でつまずいたので、そのときのメモです。

next/image は、画像のリサイズ・フォーマット変換・遅延読み込みを自動でこなしてくれる便利なコンポーネントです。ただ最初は戸惑うので、まず基本から順に整理します。

なぜ素のimgではなくnext/imageなのか

next/image を使うと、こんなことが自動で行われます。

  • 画面サイズに応じた解像度の画像を配信(必要以上に大きい画像を送らない)
  • 対応ブラウザには軽量フォーマットで配信(既定はWebP。AVIFは images.formats で有効化する)
  • ビューポート外の画像は遅延読み込み(lazy loading)
  • 寸法を先に確保してレイアウトのズレ(CLS)を防ぐ

これらを手作業でやると地味に大変なので、既定で引き受けてくれるのはありがたいです。

まず width と height を指定する

ローカルの画像をimportして使う場合は、寸法が自動で推論されます。

import Image from 'next/image'
import hero from './hero.png'

export function Hero() {
  return <Image src={hero} alt="トップのビジュアル" placeholder="blur" />
}

パス文字列で指定する場合は、widthheight を明示します。この値はレイアウトの場所を確保するために使われて、実際の表示サイズはCSSで調整できます。

<Image src="/hero.png" alt="トップのビジュアル" width={1200} height={630} />

寸法を渡しておくと、読み込み前から表示領域が確保されて、画像が入った瞬間にガクッとずれる現象を防げます。

fillは「親のサイズに合わせる」とき

縦横比が事前に分からない、あるいは親要素いっぱいに広げたいときは fill を使います。ここでつまずきやすいのですが、親要素に position: relative(など)を指定して、サイズを与えておく 必要があります。

<div className="relative h-48 w-full overflow-hidden rounded-xl">
  <Image src="/cover.jpg" alt="カバー画像" fill className="object-cover" />
</div>

fill の画像は position: absolute で配置されるので、基準となる親に relative と高さがないと、表示が崩れたり消えたりします。これは最頻出のつまずきポイントで、自分も最初「画像どこ行った?」となりました。object-coverobject-contain で、はみ出し方も指定しておきます。

sizesでムダな転送を防ぐ

fill100% 幅の画像では、sizes を指定すると配信解像度が最適化されます。sizes は「この画像が各画面幅で実際にどれくらいの幅で表示されるか」をブラウザに伝えるものです。

<Image
  src="/cover.jpg"
  alt="カバー画像"
  fill
  sizes="(max-width: 768px) 100vw, 50vw"
  className="object-cover"
/>

この例は「768px以下では画面幅いっぱい、それより広ければ画面の半分」という意味です。指定しないと大きめの画像が選ばれがちなので、レスポンシブな画像では sizes を添えておくとよさそうです。

ファーストビューの画像にはpriority

遅延読み込みは基本的に有利ですが、ファーストビューに出る最大の画像(LCP要素)は すぐ読み込んでほしい ので、priority を付けて遅延を外します。

<Image src="/hero.png" alt="" width={1200} height={630} priority />

逆に、スクロールしないと見えない画像に priority を付けると、初期表示の帯域を奪ってしまうので避けます。priority は「画面に最初から見える主役級の画像」だけに付けるのが鉄則のようです。priority を指定すると遅延読み込みが外れて、自動でプリロードもされます。

外部ドメインの画像は許可設定が必要

外部URLの画像を最適化対象にするには、next.config で許可するホストを明示します。これはセキュリティのための仕組みです。

// next.config.ts
const nextConfig = {
  images: {
    remotePatterns: [{ protocol: 'https', hostname: 'images.example.com' }],
  },
}

設定し忘れると hostname is not configured というエラーになります。エラー文がそのまま原因を教えてくれるので、外部画像を使うときは最初に確認しておくとよさそうです。

まとめ

  • ローカル画像はimportで寸法自動、パス指定なら width / height を明示
  • fill を使うときは親に relative とサイズを与える
  • レスポンシブ画像には sizes を添えてムダな転送を防ぐ
  • LCP要素になる主役画像にだけ priority を付ける(自動でプリロードされる)
  • 外部画像は remotePatterns でホストを許可する

細かいオプションはNext.js公式のnext/imageドキュメントに網羅されています。まずは fillsizes の関係を押さえておくと、つまずきの大半は避けられると思います。以上です。