ネイティブ Lazy Loading は、<img> と <iframe> に loading="lazy" を書くだけで有効になります。2026年時点で全モダンブラウザが対応済み(Baseline)で、JavaScriptライブラリはもう要りません。
ただし、ネットで見かける説明には古い情報や誤りが混ざっています。この記事では「実際に使える範囲」と「使えないのに使えると書かれがちな範囲」をはっきり分けて整理し、あわせて fetchpriority / decoding との組み合わせ方まで解説します。
スポンサーリンク
ネイティブ Lazy Loading の対応範囲
結論から言うと、loading 属性が使えるのは <img> と <iframe> の2つだけです。値は lazy と eager(既定)の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で調整します。ブラウザはwidthとheightからアスペクト比を計算して領域を確保するので、これが最も確実です。
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 にしていないか。悪化したら真っ先に疑う
- CLS:
width/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 モード自動遅延は廃止済みwidthとheightを必ず書いてCLSを防ぐ- ファーストビューの画像に lazy を付けない。むしろ
fetchpriority="high" - 読み込み開始距離は調整できない。細かく制御したいなら Intersection Observer
- フェード演出は
loadイベント+img.completeチェックで足りる
属性2つで済む話ですが、「どの画像に付けないか」の判断の方が効果に効きます。まずはページ先頭の画像から見直してみてください。