next/imageで画像を最適化するときにつまずいたこと
next/imageで画像を最適化するときにつまずいたこと
Next.jsの next/image を使い始めたとき、素の img タグと作法が違って fill や sizes でつまずいたので、そのときのメモです。
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" />
}
パス文字列で指定する場合は、width と height を明示します。この値はレイアウトの場所を確保するために使われて、実際の表示サイズは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-cover や object-contain で、はみ出し方も指定しておきます。
sizesでムダな転送を防ぐ
fill や 100% 幅の画像では、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ドキュメントに網羅されています。まずは fill と sizes の関係を押さえておくと、つまずきの大半は避けられると思います。以上です。