INP時代の画像最適化 — decoding='async'・preload・lazyの最適バランス
2024年以降、Core Web Vitals は「INP(Interaction to Next Paint)」が主役になりました。画像は 表示 の品質だけでなく操作応答 にも影響します。本稿は、INP を悪化させない画像読み込み戦略を、値の目安・実装テンプレ・検証手順まで一気通貫でまとめた実務ガイドです。
公開: 最終更新:
TL;DR(先に要点)
- LCP候補の画像は
preload
or Next/Image のpriority
とfetchpriority="high"
を併用。decoding="sync" は使わない(メインスレッドを止めやすい)。 - 折りたたみ下は
loading="lazy"
+decoding="async"
。lazy + sync はアンチパターン。 - インタラクション直前に使う画像は
img.decode()
で先行デコード(モーダルの原寸表示など)。 - 必ず
width
/height
かaspect-ratio
を指定(CLS回避は INP にも好影響)。
なぜ画像が INP を悪化させるのか
ユーザーがタップ/クリックしてから「次のペイント」までの時間が INP です。操作直後に画像の同期デコードや大きなリフローが発生すると、メインスレッドが占有され INP が悪化します。 遅延読み込み済みの画像でも、decoding
が同期だったり、sizes
不備で巨大画像を読み込むと イベント処理と競合します。
How:INPに効く画像読み込みテンプレ
1) LCP画像(ヒーロー)のテンプレ
<!-- HTML直書き -->
<link rel="preload" as="image" href="/hero.avif"
imagesrcset="/hero.avif 1x, /hero@2x.avif 2x"
fetchpriority="high">
<img src="/hero.avif" width="1600" height="900" decoding="async" alt="製品ヒーロー">
<!-- Next/Image -->
<Image
src="/hero.avif"
alt="製品ヒーロー"
width={1600} height={900}
priority
fetchPriority="high"
/>
- 元画像の実寸を
width/height
に指定し、レイアウトを先に確定。 decoding="async"
でメインスレッド占有を避ける。
2) 折りたたみ下のギャラリー
<img
src="/thumb.webp"
width="480" height="320"
loading="lazy" decoding="async"
alt="サムネイル">
loading="lazy"
は decoding="async"
とセットで。sizes
を合わせると余計な大画像を避けられます。
3) モーダルの原寸を「先にデコード」
// 操作の ~300ms 以上前に呼ぶのが目安
const large = new Image();
large.src = "/product@2x.avif";
await large.decode(); // 非同期デコードを先行
openModal(large);
4) レスポンシブの鉄則(過大ダウンロード防止)
<img
src="/hero-1600.avif"
srcset="/hero-800.avif 800w, /hero-1200.avif 1200w, /hero-1600.avif 1600w"
sizes="(min-width: 1024px) 1000px, 92vw"
width="1600" height="900"
decoding="async" fetchpriority="high" alt="">
sizes
は「レイアウト上の見込み幅」。詳しくは レスポンシブ画像の最適化 を参照。
状況別ポリシー(保存版)
状況 | 読み込み | decoding | 優先度 | 備考 |
---|---|---|---|---|
LCP候補 | preload / priority | async | fetchpriority=high | 幅は実寸、sizesを明示 |
ファーストビュー内 | 通常 | async | — | srcset/sizesで最適化 |
折りたたみ下 | lazy | async | — | IntersectionObserver相当 |
操作直前に必要 | 通常 | — | — | JSでdecode() 先行 |
計測と検証(DevTools手順)
- Performance パネルで Interaction(緑)を録画。長いタスク(>50ms)と Image Decode の重なりを確認。
- Network で LCP 画像が
Priority: High
か確認。HTTP/2 なら「最初期」に流れているか。 - Elements で LCP 画像に
width/height
oraspect-ratio
があるか確認(CLS対策)。 - web-vitals ライブラリで INP をログ収集し、デプロイ前後の差分を比較。
まとめ
画像最適化は「軽さ」だけでは不十分です。いつ・どうデコードさせるかまで設計し、INP を守りながら視覚品質を両立しましょう。本稿のテンプレを CI やコンポーネントに仕込めば、チーム全体の再現性が上がります。