1. Introduction
The Container Timing API enables monitoring when annotated sections of the DOM are displayed on screen and have finished their initial paint. A developer can mark subsections of the DOM with the containertiming attribute (similar to elementtiming for the Element Timing API) and receive performance entries when that section has been painted for the first time.
This API allows developers to measure the timing of various components in their pages. As developers increasingly organize their applications into components, there’s a growing demand to measure performance on subsections of an application or a web page.
Unlike Element Timing, it is not possible for the renderer to know when a section of the DOM has finished painting (there could be future changes, asynchronous requests for new images, slow loading buttons, etc.), so this API emits candidates in the form of PerformanceEntry objects when there has been an update.
2. Motivation
Developers want to measure when subsections of the DOM have been painted, such as tables, widgets, or other components, so they can track paint times and submit them to analytics. Current Web APIs don’t adequately support this:
-
Element Timing is limited in what it can support and cannot be used for whole sections.
-
Largest Contentful Paint (LCP) isn’t useful enough to time when specific parts of the page have loaded.
-
User-space polyfills have significant drawbacks, such as:
-
Needing to mark elements before painting (requiring server-side changes or blocking rendering)
-
Requiring a MutationObserver to catch newly injected elements
-
Needing to run in the document head, increasing time to first paint
-
Can be less efficient at rectangle tracking compared to browser built-in 2D engines
-
Web authors know their domain better than anyone else and want to communicate the performance of their own content blocks in ways their users or organization would understand (e.g., "time to first tweet").
3. Usage Example
The following example demonstrates how to register a container root and observe its paint timings.
containertiming attribute:
< div containertiming = "foobar" > < main > ...</ main > < aside > ...</ aside > </ div > < script > const observer= new PerformanceObserver(( list) => { let perfEntries= list. getEntries(); for ( const entryof perfEntries) { console. log( 'Container painted:' , entry. identifier, 'at' , entry. startTime, 'size:' , entry. size); } }); observer. observe({ entryTypes: [ "container" ] }); </ script >
The attribute should be set before the element is added to the document (in HTML, or if set in JavaScript, before adding it to the document). Setting the attribute retroactively will only capture subsequent events and future paints.
3.1. Ignoring Subtrees
containertiming-ignore> attribute:
< div containertiming = "foobar" > < main > ...</ main > <!-- Aside updates won't trigger container timing events --> < aside containertiming-ignore > ...</ aside > </ div >
4. Terminology
A container root is an Element that has the containertiming attribute.
An ignored subtree is a subtree rooted at an Element with the containertiming-ignore> attribute.
A painted region is a region (collection of rectangles) representing all the painted portions of a container root accumulated since it was first observed.
The container timing API provides timing information about when container roots are painted to the screen.
5. The PerformanceContainerTiming Interface
[Exposed =Window ]interface :PerformanceContainerTiming PerformanceEntry {readonly attribute DOMString identifier ;readonly attribute DOMRectReadOnly intersectionRect ;readonly attribute unsigned long long size ;readonly attribute DOMHighResTimeStamp firstRenderTime ;readonly attribute Element ?lastPaintedElement ; };
PerformanceContainerTiming object reports timing information for a container root.
The identifier attribute returns the value of the containertiming attribute of the container root.
The intersectionRect attribute returns a DOMRectReadOnly representing the bounding box of all paints accumulated so far within this container root.
The size attribute returns the size in pixels of the combined painted region within this container root.
The firstRenderTime attribute returns a DOMHighResTimeStamp indicating when the container root was first painted.
The lastPaintedElement attribute returns the Element that was most recently painted within the container root, or null if no element has been painted yet.
The entryType attribute must return "container".
The name attribute must return the empty string.
The startTime attribute must return the DOMHighResTimeStamp of the latest paint time for this container root.
The duration attribute must return 0.
6. Processing Model
Note: A user agent implementing the Container Timing API would need to include "container" in supportedEntryTypes for Window contexts.
This allows developers to detect support for container timing.
6.1. Extensions to the Element Interface
partial interface Element { [CEReactions ]attribute DOMString containertiming ; [CEReactions ]attribute DOMString ?; };containertimingIgnore
containertiming attribute is a DOMString that identifies the element as a container root. The value becomes the identifier in the corresponding PerformanceContainerTiming entry.
The containertiming-ignore attribute, when present, marks the element and its descendants as an ignored subtree that should not contribute to container timing measurements for ancestor container roots.
6.2. Registering Container Roots
When an Element with a [^containertiming^] content attribute is connected to the document:
-
The user agent must register the element as a container root.
-
The user agent must initialize an empty painted region for the container root.
-
The user agent must track all paint operations within the container root’s subtree, excluding any ignored subtrees.
6.3. Reporting Paint Updates
When the rendering engine paints one or more elements within a container root:
-
Let paintedRegionUpdated be false.
-
Let renderTime be the current high resolution time.
-
Let firstRenderTime be containerRoot’s stored first render time, or renderTime if not yet set.
-
If containerRoot does not have a stored first render time, set it to firstRenderTime.
-
Let paintedRegion be the painted region associated with containerRoot.
-
Let lastPaintedElement be null.
-
For each rectangle rect in rects:
-
If rect is within an ignored subtree, continue to the next rect.
-
If paintedRegion already fully contains rect, continue to the next rect.
-
Set paintedRegionUpdated to true.
-
Set paintedRegion to the union of paintedRegion and rect.
-
Set lastPaintedElement to the
Elementassociated with rect.
-
-
If paintedRegionUpdated is false, return.
-
Queue a PerformanceContainerTiming entry with:
-
identifier: the value of containerRoot’s
containertimingattribute -
startTime: renderTime
-
firstRenderTime: firstRenderTime
-
intersectionRect: the bounding rectangle of paintedRegion
-
size: the total area of paintedRegion
-
lastPaintedElement: lastPaintedElement
-
-
Let entry be a new
PerformanceContainerTimingobject with:-
entryType: "container" -
name: the empty string -
startTime: startTime -
duration: 0 -
identifier: identifier -
firstRenderTime: firstRenderTime -
intersectionRect: intersectionRect -
size: size -
lastPaintedElement: lastPaintedElement
-
-
Do not add entry to the performance timeline buffer.
Note: Container timing entries are only delivered to PerformanceObservers and are not added to the Performance Timeline buffer to avoid memory overhead from potentially frequent updates.
7. Security and Privacy Considerations
7.1. Cross-Origin Restrictions
The API respects cross-origin boundaries:
-
Elements belonging to cross-origin iframes are not exposed to parent frames.
-
No timing information crosses frame boundaries unless explicitly passed by the developer via
postMessage.
7.2. Information Exposure
Most information provided by this API can already be estimated through existing APIs:
-
Element Timing returns first rendering time for images and text.
-
Paint Timing API provides related timestamps.
-
The combination of these APIs could approximate container timing information, though less efficiently.
The API does not expose:
-
Internal implementation details of the rendering engine
-
Information about elements the developer doesn’t already have access to
-
Timing information more granular than already available through existing Performance APIs
7.3. Timing Attacks
The API uses DOMHighResTimeStamp which may be subject to resolution limitations for security purposes, consistent with other Performance APIs.
7.4. Privacy Considerations
The API does not:
-
Enable tracking users across sites
-
Expose browsing history
-
Provide information about user behavior beyond what the site already has access to through script execution
8. Acknowledgments
Many thanks for valuable feedback and advice from:
-
Barry Pollard
-
Michael Mocny
-
Scott Haseley
-
Sergey Chernyshev