ネイティブ Lazy Loading は、<img><iframe>loading="lazy" を書くだけで有効になります。2026年時点で全モダンブラウザが対応済み(Baseline)で、JavaScriptライブラリはもう要りません。

ただし、ネットで見かける説明には古い情報や誤りが混ざっています。この記事では「実際に使える範囲」と「使えないのに使えると書かれがちな範囲」をはっきり分けて整理し、あわせて fetchpriority / decoding との組み合わせ方まで解説します。

ネイティブ Lazy Loading の対応範囲

結論から言うと、loading 属性が使えるのは <img><iframe> の2つだけです。値は lazyeager(既定)の2つです。

<img src="photo.jpg" width="800" height="600" alt="サンプル画像" loading="lazy">

<iframe src="https://www.google.com/maps/embed?pb=..." width="600" height="400"
        loading="lazy" title="店舗の地図"></iframe>

 

要素 loading属性 代わりに使うもの
<img> ✅ 使える
<iframe> ✅ 使える
<video> ❌ 使えない preload="none"poster
<source> ❌ 使えない 親の <img> 側に書く
CSSの背景画像 ❌ 使えない Intersection Observer
<script> ❌ 使えない defer / async / 動的import()

<picture> を使う場合は、<source> ではなく中の <img>loading="lazy" を書きます。判定はあくまで <img> が行うためです。

<picture>
  <source srcset="photo.avif" type="image/avif">
  <source srcset="photo.webp" type="image/webp">
  <img src="photo.jpg" width="800" height="600" alt="サンプル画像" loading="lazy">
</picture>

 

「ブラウザが自動で lazy にしてくれる」は誤りです

属性を書かなければ遅延読み込みは起きません。 既定値は eager(すぐ読み込む)です。

この誤解には出どころがあります。Chrome は2019年に、Android の Lite モード(データセーバー)利用時に画面外の画像とiframeを自動で遅延読み込みする機能を入れていました。loading="auto" という値もありました。

しかしLite モードと loading="auto" はどちらも廃止済みで、Chrome は自動で遅延読み込みする予定は無いと明言しています。したがって2026年時点では、開発者が明示的に loading="lazy" を書く以外の道はありません

同様に、<video><source>loading 属性が追加された事実もありません。動画を後回しにしたい場合は次のように書きます。

<video src="movie.mp4" poster="thumbnail.jpg"
       width="640" height="360" controls preload="none"></video>

 

preload="none" で動画本体の取得を止め、poster で軽い静止画だけ先に見せる。これが動画版の遅延読み込みです。

 

width と height を必ず書く理由

書かないとレイアウトシフト(CLS)が起きます。 読み込み前の画像の高さは0なので、画像が届いた瞬間に下のコンテンツが押し下げられます。

<!-- ✅ 領域が先に確保される -->
<img src="photo.jpg" width="800" height="600" alt="サンプル画像" loading="lazy">

<!-- ❌ 画像が届いた瞬間に本文が飛ぶ -->
<img src="photo.jpg" alt="サンプル画像" loading="lazy">

 

レスポンシブで幅を可変にしたい場合も、HTML属性には実寸を書いたうえでCSSで調整します。ブラウザはwidthheightからアスペクト比を計算して領域を確保するので、これが最も確実です。

img {
  max-width: 100%;
  height: auto;
}

 

ファーストビューの画像に lazy を付けてはいけない

LCP(Largest Contentful Paint)が確実に悪化します。 ページ上部のメイン画像に loading="lazy" を付けると、ブラウザはレイアウトが確定するまで取得を始めません。最大要素の描画が数百ミリ秒〜数秒遅れます。

ファーストビューの主役画像には、むしろ優先度を上げる指定を入れます。

<!-- ページ最上部のメインビジュアル -->
<img src="hero.jpg" width="1200" height="630" alt="メインビジュアル"
     fetchpriority="high" decoding="async">

 

属性 役割 使いどころ
loading="lazy" 取得を後回しにする スクロールしないと見えない画像
fetchpriority="high" 取得の優先度を上げる LCPになるメインビジュアル
fetchpriority="low" 取得の優先度を下げる 装飾画像、下部のバナー
decoding="async" デコードでメインスレッドを止めない 基本的に全ての画像

判断基準は「スクロールせずに見えるか」の一点です。WordPressのプラグインなどで全画像に一括で loading="lazy" を付ける設定を使っている場合は、先頭の画像を除外できるか必ず確認してください。ここを外すだけでLCPが目に見えて改善することがあります。

 

どのくらい手前で読み込みが始まるのか

ブラウザはビューポートから一定距離手前で取得を開始します。この距離は固定値ではなく、実効的な回線速度によって変わります。Chrome は高速回線では短く、低速回線では長めに取る実装になっています。

つまり、開発者がしきい値を調整することはできません。「もっと早く読み込ませたい」という要件があるなら、選択肢は次のどちらかです。

  • そもそも loading="lazy" を付けない(=重要な画像である)
  • Intersection Observer で rootMargin を自分で指定する

後者の実装はIntersection Observer APIで遅延読み込みを最適化する方法にまとめています。

 

読み込み時にフェードさせたい場合

プレースホルダーからふわっと表示させたいときも、data-src方式に戻す必要はありません。loadイベントでクラスを付けるだけで済みます。

img[loading="lazy"] {
  background-color: #f2f2f2; /* 読み込み前の下地 */
  opacity: 0;
  transition: opacity .4s;
}
img[loading="lazy"].is-loaded {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  img[loading="lazy"] { opacity: 1; transition: none; }
}

 

document.querySelectorAll('img[loading="lazy"]').forEach((img) => {
  if (img.complete) {
    img.classList.add('is-loaded'); // キャッシュ済みの取りこぼしを防ぐ
    return;
  }
  img.addEventListener('load', () => img.classList.add('is-loaded'), { once: true });
});

 

img.complete のチェックが要ります。キャッシュから即座に読み込まれた画像では、スクリプトが動く前にloadが終わっていることがあるためです。これを忘れると、一部の画像だけ透明のまま残るという厄介なバグになります。

 

効果を測る方法

入れっぱなしにせず、必ず前後を測ってください。見るべき指標は次の3つです。

  • LCP:主役画像を lazy にしていないか。悪化したら真っ先に疑う
  • CLSwidth/height の書き忘れが無いか
  • 転送量:DevTools の Network タブで、初期表示時の画像リクエスト数が減っているか

Lighthouse は「Defer offscreen images」と「Largest Contentful Paint image was lazily loaded」の両方を指摘してくれるので、この2つが同時に出ていないかを確認するのが手早いです。

 

まとめ

  • loading 属性が効くのは <img><iframe> だけ<video><source>には無い
  • 既定は eager書かなければ遅延読み込みは起きない(自動でlazyになるブラウザは無い)
  • loading="auto" と Chrome の Lite モード自動遅延は廃止済み
  • widthheight を必ず書いてCLSを防ぐ
  • ファーストビューの画像に lazy を付けない。むしろ fetchpriority="high"
  • 読み込み開始距離は調整できない。細かく制御したいなら Intersection Observer
  • フェード演出は load イベント+img.complete チェックで足りる

属性2つで済む話ですが、「どの画像に付けないか」の判断の方が効果に効きます。まずはページ先頭の画像から見直してみてください。

ABOUT ME
りん
このブログでは、Web開発やプログラミングに関する情報を中心に、私が日々感じたことや学んだことをシェアしています。技術と生活の両方を楽しめるブログを目指して、日常で触れた出来事や本、グルメの話題も取り入れています。気軽に覗いて、少しでも役立つ情報や楽しいひとときを見つけてもらえたら嬉しいです。