The Largest Contentful Paint metric tells you when the biggest visible element on the page finishes painting. But what if you want to know when a specific product card, a news article's body, or a data dashboard renders — not just the page's biggest element?
v145 · Performance · JavaScript · demo
Blog Post
A realistic blog post template with four named containertiming sections — hero, body, code block, and sidebar. A PerformanceObserver watches for entries as each section finishes painting and builds a live timing waterfall, showing which content block painted first and how long each took.
containertiming attribute
and PerformanceContainerTiming entries when PerformanceObserver
advertises "container". Other browsers use a DOM-geometry simulation so the
measurement workflow remains testable.
Replay the article load, toggle the ignored sidebar subtree, or probe observer support · the bar width represents each section's paint time relative to the total
Container Timing introduces the containertiming HTML attribute. Add it to any element and a PerformanceObserver receives a PerformanceContainerTiming entry the moment that section finishes its first contentful paint. The entry includes a renderTime, size, and the identifier you provided.
This makes it straightforward to measure things that LCP can't: "how long did our hero image block take?", "when was the sidebar populated?", or "did our above-the-fold content paint before the 1-second mark?"
// Set up the observer
const observer = new PerformanceObserver(list => {
for (const entry of list.getEntries()) {
console.log(entry.identifier, entry.renderTime);
}
});
observer.observe({ type: 'container', buffered: true });
// Mark sections with the HTML attribute:
// <section containertiming="hero"></section>
// Container Timing draft API
// <section containertiming="hero">...</section>
// <aside containertiming-ignore>...</aside>
const observer = new PerformanceObserver(list => {
for (const entry of list.getEntries()) {
// PerformanceContainerTiming entry:
console.log({
identifier: entry.identifier, // "hero"
entryType: entry.entryType, // "container"
startTime: entry.startTime, // equals renderTime
firstRenderTime: entry.firstRenderTime,
size: entry.size, // painted area in CSS px²
rootElement: entry.rootElement,
lastPaintedElement: entry.lastPaintedElement
});
}
});
observer.observe({
type: 'container',
buffered: true // capture already-painted sections
});