画像とメディアがレイアウトシフトを引き起こす原因(およびその修正方法)
画像、動画、iframe、レスポンシブ画像、およびメディアの埋め込みによるCLSを防ぐための完全ガイド

画像とメディアがレイアウトシフトを引き起こす原因(およびその修正方法)
2025 Web Almanacは、私が現場で見続けている状況を数値化しています。すなわち、モバイルページの62%には、明示的なwidthとheightを持たない画像が少なくとも1つ存在します。これにより、サイズ指定のないメディアはウェブ上のCumulative Layout Shiftの最大の原因となっています。そして、これらのシフトはすべて、2019年から存在するテクニックで防ぐことが可能です。
最終更新:2026年3月 (Arjen Karel)
ブラウザは画像の大きさを知らない
画像によって引き起こされるすべてのレイアウトシフトは、1つの原因に帰結します。それは、画像が読み込まれる前に、ブラウザがどれだけのスペースを確保すべきかを知らないということです。
ブラウザがサイズ指定のない<img>タグに遭遇したとき、画像のサイズがわかるまで処理を停止するわけではありません。単に0x0ピクセルのスペースを確保するだけです。その後画像がダウンロードされると、ブラウザはレイアウトを再計算し、画像より下にあるすべての要素が下に押し下げられます。その動きこそがCLSです。

widthとheightがレイアウトシフトをどう防ぐのか
レイアウトシフトの唯一の修正方法は、適切なスペースを確保することです。それを行う最も簡単な方法は、画像の正しい(本来の)widthとheightを設定することです。2019年と2020年に、すべての主要なブラウザはwidth属性とheight属性の動作を変更する機能を導入しました。現在、ブラウザはこれらを使用して、画像がダウンロードされる前にアスペクト比を計算します。
以下のように記述すると:
<img src="hero.jpg" width="800" height="450" alt="Description">
ブラウザは内部で以下を生成します:
img[Attributes Style] {
aspect-ratio: auto 800 / 450;
}
レスポンシブ画像の場合は、このCSSを追加します:
img {
height: auto;
max-width: 100%;
}
ブラウザは寸法を計算するために画像ファイル全体をダウンロードする必要はありません。ブラウザは比率を把握し、垂直方向のスペースを確保します。CSSは最後の部分、つまり包含(containment)を担います。height: autoは比率から高さを計算します。max-width: 100%は画像がコンテナからはみ出すのを防ぎます。
修正を無効にしてしまう要因
画像がレイアウトシフトを引き起こすのを防ぐために必要なのは、widthとheight属性だけです。しかし、寸法とCSSを設定していても、その効果を打ち消し、画像がレイアウトシフトを引き起こす原因となる一般的なパターンがいくつか存在します。
CSSのwidth:auto
これは私が最も頻繁に目にするものであり、デバッグの時間を最も無駄にする原因です。開発者はすべての画像にwidthとheightを設定し、HTMLで正しい処理を行いますが、CSSファイルのどこかに画像に対する余分なwidth:autoが存在しているのです。
img {
width: auto;
height: auto;
max-width: 100%;
}
問題はwidth: autoです。ブラウザの内部比率はCSSの優先順位が最も低いため、いかなるルールでも上書きされてしまいます。width: autoは、ブラウザが高さを計算するために使用していた幅を削除します。その結果、両方の寸法が不明になります。ファイルがダウンロードされて最終的な寸法が判明するまで、画像は0x0としてレンダリングされます。
CSSでaspect-ratioを設定しても、この問題は解決しません。width: autoがあるため、ブラウザは初期状態では幅を0として扱います。0から計算されたアスペクト比は依然として0x0を生成するだけです。
この問題の発見を難しくしているのは、ブラウザのキャッシュです。画像がブラウザのキャッシュにある場合、実際の寸法がすぐに利用できるため、シフトは発生しません。私は何十ものクライアントのサイトでこれをデバッグしてきましたが、常に開発者のマシンにキャッシュされていました。
修正方法:
img {
height: auto;
max-width: 100%;
}
width: autoを削除します。height: autoとmax-width: 100%のみを維持します。これはweb.devが推奨するパターンです。
簡単な確認方法:任意の画像を右クリックして検査(Inspect)し、計算済み(computed)のスタイルを確認します。width: autoが表示されていれば、それが問題の原因です。完全な手順については、自動サイズ調整画像によるレイアウトシフトの修正を参照してください。
誤った画像の寸法
内部で生成されるアスペクト比を覚えていますか?ここから少し専門的になります。autoキーワードはブラウザに対して、「この比率をプレースホルダーとして使用し、実際の画像が読み込まれたら実際の寸法に切り替える」よう指示します。誤った値(16:9の画像に対してwidth="4" height="3")を設定した場合、ブラウザは最初は4:3のスペースを確保し、画像が読み込まれたときに16:9に補正します。その補正こそがレイアウトシフトです。常に画像の実際のピクセル寸法を使用してください。
CSSのaspect-ratioがより良い選択となる場合
width/height属性はデフォルトのアプローチであり常に最良のアプローチですが、CMSが画像の寸法を追加させてくれない場合があります(そしてそれは想像以上に頻繁に起こります!)。そのような場合、CSSのaspect-ratioを使用することで、確保されるスペースを制御できます。たとえば、ヒーロー画像に.heroクラスがある場合、次のようにスペースを確保できます:
img.hero {
aspect-ratio: 16 / 9;
width: 100%;
}
これはすべてのモダンブラウザ(Chrome 88以降、Firefox 87以降、Safari 15以降)で機能し、画像や動画だけでなく任意の要素で機能します。
動画、iframe、およびSVG
動画
同じ問題であり、修正方法も同じです。動画の解像度に合わせてwidthとheightを設定します:
<video src="demo.mp4" width="1280" height="720" autoplay muted loop></video>
CSSにheight: auto; max-width: 100%;を追加します。1つ注意点があります。ポスター画像ではなく、動画の解像度に合わせて寸法を設定してください。これらが異なると、再生開始時にシフトが発生します。
iframe
画像とは異なり、iframeは属性からアスペクト比を計算しません。明示的な寸法がない場合、デフォルトで300x150ピクセルになります。ほとんどの埋め込みにおいて、それは誤りです。iframeの場合は、次のようにaspect-ratioを設定するのが最良です:
.responsive-iframe {
width: 100%;
height: auto;
aspect-ratio: 16 / 9;
}
さらに良いのは、iframeをまったく読み込まないことです。YouTube、Vimeo、Google Maps、およびSNSの埋め込みについて、私は何年も前にページロード時のiframe読み込みをやめました。正しいアスペクト比を持つ静的なプレースホルダー画像を表示します。iframeが可視領域に入ったとき、JavaScriptを使ってそれを実際のiframeと入れ替えます。入れ替えによるシフトはユーザー入力から500ミリ秒以内に発生するため、仕様によりCLSから除外されます。
実装の詳細については、完璧なYouTube埋め込みおよびPageSpeedを損なわないGoogle Mapsを参照してください。
SVG
<img>経由で読み込まれるSVGには、ラスター画像と同様にタグにwidthとheightが必要です。インラインの<svg>要素には、CSSのaspect-ratioとともにviewBoxが必要です。どちらもない場合、デフォルトで300x150になります。
レスポンシブ画像
すべてのsrcsetソースで同じ比率を維持する
srcset内のすべての画像は、同じアスペクト比を共有している必要があります。そうであれば、<img>に対する1組のwidth/heightだけで十分です:
<img
src="hero-800.jpg"
srcset="hero-400.jpg 400w, hero-800.jpg 800w, hero-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, 800px"
width="800" height="450"
alt="Hero image">
800:450は、3つのバリエーションすべてで同じ比率です。ブラウザがどれを選択しても、確保されるスペースは正しくなります。異なる比率が必要な場合は、代わりに<picture>と<source>要素を使用してください。
アートディレクション:ブレークポイントごとに異なる比率
ビューポートの幅に応じて異なる切り抜き(クロップ)を提供する場合は、<picture>要素を使用する必要があります。それぞれの<source>にwidthとheightを設定します:
<picture>
<source
media="(max-width: 799px)"
srcset="hero-mobile.jpg"
width="480" height="600">
<source
media="(min-width: 800px)"
srcset="hero-desktop.jpg"
width="1200" height="400">
<img
src="hero-desktop.jpg"
width="1200" height="400"
alt="Product hero image">
</picture>
ChromeとSafariは、アクティブになっている<source>から正しい寸法を取得します。しかし、Firefoxはそうではありません(バグ 1694741)。Firefoxは常にフォールバックである<img>の寸法を使用します。回避策:CSSメディアクエリでブレークポイントを一致させます。
picture img {
width: 100%;
height: auto;
}
@media (max-width: 799px) {
picture img {
aspect-ratio: 480 / 600;
}
}
@media (min-width: 800px) {
picture img {
aspect-ratio: 1200 / 400;
}
}
すべての切り抜きが同じ比率を共有している場合、このFirefoxのバグは問題になりません。
固定コンテナ、カルーセル、および封じ込め(Containment)
固定サイズコンテナのためのobject-fit
すべてのカードが同じ高さで、画像のアスペクト比が異なる製品カードのグリッドの場合。コンテナをロックし、画像でそれを満たします:
.product-image {
width: 100%;
aspect-ratio: 1 / 1;
object-fit: cover;
object-position: center;
}
<img
src="product.jpg"
width="400" height="600"
class="product-image"
alt="Product name">
画像が読み込まれる前にサイズが固定されます。これはCSSの背景画像(background images)の代わりにもなります。背景画像は遅延読み込み(lazy load)ができず、プリロードスキャナー(preload scanner)はそれらを見つけることができず、fetchpriorityを使用することもできません。object-fit: coverを指定した<img>を使用すれば、これらのすべての制御が可能になります。
カルーセル
left、width、またはmarginをアニメーション化するスライドトランジションは、レイアウトの再計算を引き起こします。自動再生はユーザー入力ではないため、すべてのシフトがCLSとしてカウントされます。コンテナを固定アスペクト比で固定してください。代わりにtransform: translateX()を使ってアニメーション化します。TransformはGPU上で実行され、レイアウトを引き起こすことは決してありません。
制御できない埋め込みの封じ込め
広告枠、サードパーティのウィジェット、ユーザー生成コンテンツ。これらが何をレンダリングするかをあなたは制御できず、クリップすることもできません。現実的な目標は、シフトをゼロにすることではなく、最小限に抑えることです。
まずはスペースを確保することから始めます:
.ad-slot {
min-height: 250px;
contain: layout style;
}
min-heightは大きな成果をもたらします。広告が250px以下で読み込まれた場合、シフトは発生しません。300pxで読み込まれた場合、ゼロから300pxのシフトが発生する代わりに、50pxのシフトで済みます。この違いは重要です。
contain: layoutはまた別の働きをします。コンテナが拡大するのを止めるわけではありません。内部で起こることを隔離するのです。広告ネットワークが自身のコンテンツをリフローする(iframeのリサイズ、新しい要素の挿入、内部レイアウトの再計算など)スクリプトを注入した場合、それらの再計算はコンテナ内に留まります。containmentがないと、広告内のすべての内部リフローがページ全体のレイアウト再計算を引き起こします。これがあれば、ブラウザはボックスの外側にあるすべてをスキップします。これにより、広告が読み込まれている間のページの応答性が向上します。
contain: styleは、CSSカウンターやその他のスタイルの副作用が外部に漏れるのを防ぎます。手軽な保険のようなものです。
min-heightの値については、広告プロバイダーの最も一般的なクリエイティブのサイズを確認し、インプレッションの大部分をカバーするものを選択してください。広告の90%が250pxで10%が300pxの場合、250pxに設定し、時折発生する小さなシフトを受け入れます。これを300pxに設定すると、ページ読み込みの90%で、小さい広告が読み込まれたときに50pxの空白スペースが崩壊することになります。その崩壊もまたレイアウトシフトです。
広告に対する完璧な答えはありません。目標は、ほとんどのページ読み込みにおいて可能な限り小さなシフトに抑えることです。
画像のCLSを見つける方法
通常のブラウジング条件では画像のCLSを捉えることはできません。ブラウザのキャッシュには以前の訪問時の寸法がすでに存在するため、シフトは決して発生しないからです。実際のユーザーが見ているものを確認するには、DevTools(F12)を開き、ネットワーク(Network)タブに移動して「キャッシュを無効にする(Disable cache)」にチェックを入れます。キャッシュはDevToolsが開いている間のみ無効になります。または、シークレットウィンドウを使用してください。
Real User Monitoring
CoreDashまたはその他のRUMツールから始めましょう。CLSの属性(attribution)データは、どの要素がシフトしたかを正確に示します。CLSに移動し、属性の要素(Element)テーブルを確認します。画像でフィルタリングすると、レイアウトシフトの影響を受けたすべての画像要素が、影響度の順に表示されます。

Chrome DevTools
ネットワークタブでキャッシュを無効にし、Slow 4Gにスロットリングして、スクリーンショットを有効にし、リロードします。視覚的なジャンプがないか監視します。その後、パフォーマンス(Performance)パネルを開き、「Layout Shift」のエントリを探します。シフトをクリックすると、ノード、スコア、および最近のユーザー入力があったかどうかを確認できます。
Core Web Vitals Visualizer
Core Web Vitals Visualizer拡張機能は、すべてのレイアウトシフトを色付きのオーバーレイでハイライトします。私はこれをパフォーマンスパネルを開く前の最初のステップとして使用しています。拡張機能をアクティブにした状態でリロードすると、何が移動したかを正確に確認できます。
CLSをすばやく修正するためのチェックリスト
| 要素 | CLSの原因 | 修正方法 |
|---|---|---|
<img> |
width/heightの欠落 | widthとheightを追加する;CSSでheight: auto; max-width: 100%;を使用する |
<img> |
CSSのwidth: autoが寸法を上書きしている |
width: autoを削除する;height: autoのみを維持する |
<img> |
誤ったwidth/heightの値 | 画像の実際のピクセル寸法を使用する |
<video> |
width/heightの欠落 | 動画の解像度に一致するwidthとheightを追加する |
<iframe> |
デフォルトの300x150 | CSSのaspect-ratio: 16 / 9; width: 100%;またはファサードパターン |
<picture> |
ソースごとの異なる比率(Firefoxのバグ) | 各<source>にwidth/heightを設定する;CSSメディアクエリのフォールバックを追加する |
<img srcset> |
srcset内の混在した比率 | すべてのソースで同じ比率にする;<img>にwidth/heightを設定する |
| JS遅延読み込み(lazy loading) | 誤った比率の1x1プレースホルダー | ネイティブのloading="lazy"を使用するか、プレースホルダーの比率を一致させる |
| カルーセル | 自動再生 + レイアウトを引き起こすトランジション | 固定アスペクト比のコンテナ;トランジションにはtransformを使用 |
| SVG | 組み込みの寸法がない | <img>にwidth/heightを設定するか、viewBox + CSSのaspect-ratioを使用 |
| 広告枠 / 埋め込み | 不明な寸法 | min-height + contain: layout style |
画像のCLSに関するウェブの現状
2025 Web Almanacの数値:
- モバイルページの62%に、サイズ指定のない画像が少なくとも1つある。2024年の66%から減少したが、依然として過半数を占める。
- デスクトップページの65%に、サイズ指定のない画像がある。69%から減少。
- パーセンタイル75(p75)において、デスクトップページには9つのサイズ指定のない画像があり、モバイルには8つある。
- サイズ指定のない画像の中央値の高さ:デスクトップで111px、モバイルで98px。段落をシフトさせるには十分な高さ。
- デスクトップの72%、モバイルのオリジンの81%がCLSに合格している。2021年の62%から上昇。
過去4年間で、CLSは他のどのCore Web Vitalsよりも改善されました。しかし、サイズ指定のない画像は依然として最大の要因です。この1つの問題を修正すれば、ほとんどのサイトでレイアウトシフトは消滅します。
関連ガイド
- Cumulative Layout Shift (CLS) とは:完全ガイド。計算式、しきい値、セッションウィンドウ、および画像以外のすべてのCLSの原因。
- CLS問題の発見と修正:RUMデータ、DevToolsを使用したステップバイステップの診断、およびすべての原因に対する修正方法。
- 自動サイズ調整画像によるレイアウトシフトの修正:
width: autoに関する完全な解説。 - Core Web Vitalsのための画像最適化:プリロード、レスポンシブ画像、フォーマット、優先順位付け。
- CSSトランジションによるレイアウトシフト:レイアウトを引き起こすアニメーションがCLSをどのように発生させるか。
- 完璧なYouTube埋め込み:CLSをゼロにするファサードパターン。
- PageSpeed 100%のGoogle Maps:Mapsに対する同様のファサードアプローチ。
画像とメディアのCLSに関するよくある質問
widthとheight属性が設定されていても、画像へのwidth:autoがレイアウトシフトを引き起こすのはなぜですか?
width/height属性から得られるブラウザの内部的なアスペクト比は、CSSの優先順位が最も低いためです。width: autoはそれを上書きし、両方の寸法を不明にします。ファイルがダウンロードされるまで、画像は0x0でレンダリングされます。width: autoを削除し、height: auto; max-width: 100%;のみを維持してください。
動画やiframe要素にもwidthとheightは必要ですか?
動画については「はい」です。メカニズムは同じです。iframeは異なります。属性から比率を計算することはなく、デフォルトで300x150になります。CSSのaspect-ratioか、ファサードパターンを使用してください。
ブレークポイントごとにアスペクト比が異なる場合、picture要素でのCLSをどのように防げばよいですか?
各<source>にwidthとheightを設定します。ChromeとSafariは正しい寸法を使用します。Firefoxには、常にフォールバックの<img>を使用するというバグがあります。回避策として、ブレークポイントごとに正しいaspect-ratioを指定したCSSメディアクエリを追加してください。
遅延読み込み(lazy loading)はレイアウトシフトを引き起こしますか?
画像にwidthとheight属性があれば引き起こしません。しかし、ファーストビュー(above-the-fold)の画像を遅延読み込みさせると、何のメリットもなくLCPが遅延します。初期のビューポートにある画像には絶対にloading="lazy"を使用しないでください。
Lighthouseでは良好なCLSが示されるのに、フィールドデータでレイアウトシフトが示されるのはなぜですか?
Lighthouseは、高速なネットワークとウォーム状態のブラウザで実行されます。計算されたCSSスタイルではなくHTML属性をチェックするため、width: autoの問題を捕捉できません。CLSは常にCrUXのフィールドデータか、CoreDashのようなRUMツールで検証してください。

