Largest Contentful Paint (LCP)
歴史的に見ても、Web ページのメイン コンテンツがどの程度早く読み込まれ、ユーザーに対して表示されるかを測定することは Web 開発者にとっての大きな課題でした。
load や DOMContentLoaded のような古い指標は、ユーザーが画面上で見ているものとは必ずしも一致しないため、 適切ではありません。また、First Contentful Paint (視覚コンテンツの初期表示時間、FCP) のようなユーザーを中心とした新しいパフォーマンス指標は、読み込みエクスペリエンスのごく初期の部分しかキャプチャできません。特にページにスプラッシュ スクリーンや読み込みインジケーターなどが表示されている場合には、この瞬間的な出来事はユーザーにとってあまり意味がありません。
過去には、初期表示後の読み込みエクスペリエンスを把握するために First Meaningful Paint (意味のある視覚コンテンツの初期表示時間、FMP) や Speed Index (スピード インデックス、SI) といったパフォーマンス指標を推奨してきました。しかしながら、これらの指標は複雑で説明が難しく、間違っている場合も多かったため、これらを使用してページのメイン コンテンツがいつ読み込まれたかを特定することはできませんでした。
物事には、シンプルな方が好ましい場合があります。W3C Web Performance ワーキング グループでの議論や Google が実施した調査によると、ページのメイン コンテンツの読み込みタイミングをより正確に測定するためには、最も大きい要素がレンダリングされるタイミングの測定が重要であることが分かりました。
LCP とは? #
Largest Contentful Paint (LCP) 指標は、ビューポート内に表示される最も大きい画像またはテキスト ブロックのレンダリング時間を、ページの初期読み込み開始タイミングと比較してレポートします。
LCP における良いスコアとは? #
良好なユーザー体験を提供するために、サイトは Largest Contentful Paint が 2.5 秒以下になるように努力する必要があります。ほぼすべてのユーザーに対してこの目標値を確実に達成するためには、モバイル デバイスとデスクトップ デバイスに分けた上で、総ページロード数の 75 パーセンタイルをしきい値として設定します。
どのような要素が考慮されますか? #
Largest Contentful Paint API で現在規定されているように、Largest Contentful Paint については以下の要素が考慮されます。
<img>
要素<svg>
要素内の<image>
要素<video>
要素 (ポスター画像が使用されます)- (CSS グラデーションとは対照的に、)
url()
関数を介して読み込まれた背景画像が含まれている要素 - テキスト ノードやその他のインラインレベルのテキスト要素の子要素を含むブロックレベル要素。
なお、要素をこのように限定したのは、最初に考え方をシンプルにするという意図があったためです。研究が進むにつれて、追加の要素 (<svg>
、<video>
など) が今後追加されていく可能性があります。
要素のサイズはどのようにして決定されますか? #
Largest Contentful Paint としてレポートされる要素のサイズは、通常ビューポート内でユーザーに対して表示されるサイズとなります。要素がビューポートからはみ出していたり、要素の一部が切り取られていたり、画面に表示されないオーバーフローが発生したりしているような場合、そういった部分は要素のサイズには含まれません。
本来のサイズからリサイズされた画像要素については、表示されたサイズと本来のサイズのうち、いずれか小さい方がレポートされます。たとえば、本来のサイズよりもはるかに小さいサイズへと縮小された画像については、表示されたサイズのみがレポートされ、逆に大きなサイズへと引き伸ばされたり拡大されたりした画像については、本来のサイズのみがレポートされます。
テキスト要素については、そのテキスト ノードのサイズ (すべてのテキスト ノードを包含する最小の長方形) のみが考慮されます。
あらゆる要素において、CSS を介して適用されているマージン、パディング、ボーダーはすべて考慮されません。
最大視覚コンテンツはどのタイミングでレポートされますか? #
Web ページは段階的に読み込まれる場合が多く、その結果としてページ内で最も大きい要素が変更される可能性があります。
こういった変更の可能性に対応するため、ブラウザーは最初のフレームを描画した直後に、最大のコンテンツ要素を特定する largest-contentful-paint
タイプの PerformanceEntry
をディスパッチします。しかしながら、後続するフレームのレンダリング後に最大のコンテンツ要素が変更されるたびに、別の PerformanceEntry
をディスパッチします。
たとえば、テキストとヒーロー画像があるページでは、ブラウザーはテキストを最初にレンダリングします。その時点でブラウザーは largest-contentful-paint
エントリをディスパッチしますが、その element
プロパティはおそらく <p>
または <h1>
を参照するはずです。その後、ヒーロー画像の読み込みが完了すると 2 番目の largest-contentful-paint
エントリがディスパッチされ、その element
プロパティは <img>
を参照するはずです。
重要なのは、ある要素についてレンダリングが完了し、ユーザーに対して表示された時点で初めて、その要素は最大のコンテンツ要素としてみなされるという点です。まだ読み込まれていない画像は、"レンダリングされた" とはみなされません。また、フォント ブロック期に Web フォントを使用するテキスト ノードについても同様です。このような場合に、比較的サイズが小さい要素が最大のコンテンツ要素としてレポートされる可能性がありますが、よりサイズの大きい要素のレンダリングが完了すれば、別の PerformanceEntry
オブジェクトを介してすぐにその要素がレポートされます。
遅れて読み込まれる画像やフォントに加えて、新しいコンテンツが利用可能になったタイミングでページが新しい要素を DOM に追加する場合があります。こういった新しい要素のいずれかがそれまでの最大のコンテンツ要素よりもサイズが大きくなる場合、新しい PerformanceEntry
もレポートされます。
現時点での最大のコンテンツ要素がビューポートから削除された場合 (あるいは DOM から削除された場合)、よりサイズの大きい要素がレンダリングされない限り、その要素が引き続き最大のコンテンツ要素として見なされます。
ブラウザーは、ユーザーが (タップ、スクロール、キー押下などにより) ページを操作すると、すぐに新しいエントリのレポートを停止します。ユーザーの操作によって、ユーザーに対して表示される内容が変更される場合がよくあるからです (特にスクロールの場合)。
分析を行う場合には、直近にディスパッチされた PerformanceEntry
のみをアナリティクス サービスにレポートする必要があります。
読み込み時間とレンダリング時間 #
セキュリティ上の理由から、Timing-Allow-Origin
ヘッダーを持たないクロスオリジン画像では画像のレンダリングのタイムスタンプは公開されません。その代わりに、読み込み時間のみが公開されます (この情報はその他多くの Web API を介してすでに公開されているためです)。
以下の使用例では、レンダリング時間が利用できない要素の処理方法について示しています。ただし、可能な限りTiming-Allow-Origin
ヘッダーを設定することを常に推奨しています。そうすることで、指標はより正確になります。
要素のレイアウトやサイズの変更は、どのように処理されますか? #
新しいパフォーマンス エントリを計算してディスパッチする際に発生するパフォーマンス オーバーヘッドを低く抑えるために要素のサイズや位置を変更しても、新しい LCP 候補は生成されません。要素の初期サイズとビューポート内での位置のみが考慮されます。
つまり、最初は画面外にレンダリングされ、その後で画面内に移動した画像についてはレポートされない可能性があります。また、最初はビューポート内にレンダリングされていた要素が押し下げられて表示されなくなった場合でも、ビューポート内での初期サイズがレポートされます。
例 #
ここでは、いくつかの人気 Web サイトにおいて Largest Contentful Paint が発生する場合の例をご紹介します。


上記 2 つのタイムラインでは、コンテンツが読み込まれると、最大の要素が変更されます。1 つ目の例では新しいコンテンツが DOM に追加され、それにより最大の要素が変更されています。2 つ目の例ではレイアウトが変更され、それまで最も大きかったコンテンツがビューポートから削除されています。
遅れて読み込まれたコンテンツの方がすでにページ上に表示されているコンテンツよりもサイズが大きいといったケースはよく見られますが、必ずしもそうなるわけではありません。次の 2 つの例では、ページが完全に読み込まれる前に Largest Contentful Paint が発生しています。


1 つ目の例では、Instagram のロゴの読み込みが比較的早い段階で完了し、その他のコンテンツが徐々に表示されていく中でもロゴが最大の要素となっています。Google の検索結果ページの例では、画像やロゴの読み込みが完了する前に表示されるテキストの段落が最大の要素となっています。個々の画像はすべてこの段落よりもサイズが小さいため、読み込みプロセス全体を通してこの段落がずっと最大の要素となっています。
LCP の測定方法 #
LCP はラボ環境または実際のユーザー環境で測定が可能で、以下のツールが使用できます。
フィールド測定を実施するためのツール #
- Chrome User Experience Report
- PageSpeed Insights
- Search Console (Core Web Vitals Report)
web-vitals
JavaScript ライブラリ
ラボ測定を実施するためのツール #
JavaScript を使用して LCP を測定する #
- Chrome 77, Supported 77
- Firefox, Not supported
- Edge 79, Supported 79
- Safari, Not supported
JavaScript を使用した LCP の測定には、Largest Contentful Paint API を使用することができます。以下の例では、largest-contentful-paint
エントリをリッスンしてコンソールにログを記録する PerformanceObserver
の作成方法を示しています。
new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
console.log('LCP candidate:', entry.startTime, entry);
}
}).observe({type: 'largest-contentful-paint', buffered: true});
上記の例では、ログとして記録された各 largest-contentful-paint
エントリが現時点での LCP 候補を表しています。一般的には最後に発行されたエントリの startTime
値が LCP 値となりますが、必ずしもそうなるわけではありません。すべての largest-contentful-paint
エントリが LCP の測定に有効なわけではありません。
次のセクションでは、API がレポートする内容と、指標の計算方法の違いについて説明します。
指標と API の違い #
- API はバックグラウンド タブで読み込まれているページに対して
largest-contentful-paint
エントリをディスパッチしますが、LCP を計算する場合にはそういったページを無視する必要があります。 - API はページがバックグラウンドに移行した後も
largest-contentful-paint
エントリをディスパッチし続けますが、LCP を計算する場合にはこういったエントリを無視する必要があります (要素が考慮されるのは、ページがずっとフォアグラウンドにあった場合のみです)。 - API は、ページが Back/Forward Cache から復元された場合の
largest-contentful-paint
エントリはレポートしませんが、これらはユーザーにとっては別々のページ訪問となるため、こういったケースにおいても LCP は測定される必要があります。 - API では iframe に含まれている要素は考慮されませんが、LCP を正確に測定するためにはこれらの要素も考慮に入れる必要があります。サブフレームが集約のために API を使用してその親フレームに
largest-contentful-paint
エントリをレポートすることができます。
こういった微妙な違いをすべて記憶していなくても、web-vitals
JavaScript ライブラリを使用して LCP を測定すれば、これらの違いを (可能な限り) 処理してくれます。
import {onLCP} from 'web-vitals';
// 実行可能となった時点ですぐに LCP の測定やログ記録を実行します。
onLCP(console.log);
JavaScript を使用して LCP を測定する方法に関する詳細な例については、onLCP()
のソース コードを参照してください。
最も重要な要素が最もサイズの大きい要素ではない場合 #
場合によっては、ページ上で最も重要な要素が最もサイズの大きい要素ではないため、開発者の方がこれらの要素以外の要素のレンダリング時間を測定したいと考える可能性があります。これは、「Custom Metrics (カスタム指標)」の記事で説明されている Element Timing API を使用すれば可能です。
LCP の改善方法 #
LCPは、主に次の 4 つの要因の影響を受けます。
- サーバーの応答時間が遅い
- レンダリングを妨げる JavaScript および CSS
- リソースの読み込み時間
- クライアント側のレンダリング
LCP の改善方法の詳細については、「LCP を最適化する」を参照してください。LCP の改善にもつながる個別のパフォーマンス改善手法に関するその他のガイダンスについては、以下を参照してください。
- PRPL パターンを使用して読み込みを高速化する
- クリティカル レンダリング パスの最適化
- CSS を最適化する
- 画像を最適化する
- Web フォントを最適化する
- JavaScript を最適化する (クライアント サイドでレンダリングを行うサイト向け)
その他のリソース #
- Annie Sullivan による「Chrome のパフォーマンス監視から得られた教訓」performance.now() より (2019)
CHANGELOG #
Occasionally, bugs are discovered in the APIs used to measure metrics, and sometimes in the definitions of the metrics themselves. As a result, changes must sometimes be made, and these changes can show up as improvements or regressions in your internal reports and dashboards.
To help you manage this, all changes to either the implementation or definition of these metrics will be surfaced in this CHANGELOG.
If you have feedback for these metrics, you can provide it in the web-vitals-feedback Google group.