Container Timing API

Editor’s Draft,

More details about this document
This version:
https://bloomberg.github.io/container-timing/
Test Suite:
https://github.com/web-platform-tests/wpt/tree/master/container-timing
Issue Tracking:
GitHub
Editors:
Jason Williams (Bloomberg)
(Igalia)

Abstract

This specification defines an API that enables monitoring when annotated sections of the DOM are displayed on screen and have finished their initial paint.

Status of this document

This is a public copy of the editors’ draft. It is provided for discussion only and may change at any moment. Its publication here does not imply endorsement of its contents by W3C. Don’t cite this document other than as work in progress.

GitHub Issues are preferred for discussion of this specification.

This document is governed by the 18 August 2025 W3C Process Document.

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:

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").

2.1. Life Cycle

In this example life cycle a component will paint multiple pieces of content at different times, each of these times will generate a new PerformanceContainerTiming entry with updated information.

However, once a region has been painted, subsequent paints of that same region will not generate new entries. Container Timing Life Cycle Diagram

3. Usage Example

The following example demonstrates how to register a container root and observe its paint timings.

Registration is on a per-element basis using the containertiming attribute:
<div containertiming="foobar">
  <main>...</main>
  <aside>...</aside>
</div>

<script>
  const observer = new PerformanceObserver((list) => {
    let perfEntries = list.getEntries();
    for (const entry of 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

Parts of the DOM tree can be ignored using the <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;
    readonly attribute Element? rootElement;
};

PerformanceContainerTiming includes PaintTimingMixin;
Each PerformanceContainerTiming object has these associated concepts:

The entryType attribute’s getter must return the DOMString "container".

The name attribute’s getter must return the empty string.

The duration attribute must return 0.

The startTime attribute’s getter must return the value of this’s renderTime.

The identifier attribute must return the value of this’s identifier.

The intersectionRect attribute must return the value of this’s intersectionRect.

The size attribute must return the value of this’s size.

The firstRenderTime attribute must return the value of this’s firstRenderTime.

The lastPaintedElement attribute must return the value of this’s lastPaintedElement.

The rootElement attribute must return the value of this’s rootElement.

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. Per-Document State

For each Document, the user agent must maintain a container root records map that maps container root Elements to Container Timing Record objects.

6.2. Extensions to the Element Interface

This section will be removed once the [DOM] specification had been modified.

We extend the Element interface as follows:

partial interface Element {
    [CEReactions] attribute DOMString containertiming;
    [CEReactions] attribute DOMString? containertimingIgnore;
};
The 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.3. Container Timing Record

This specification defines an internal data structure used by the processing model:

A Container Timing Record has these associated concepts:
  • A paintTimingInfo which is a paint timing info.

  • An identifier which is a DOMString.

  • A paintedRegion which is a painted region, initially empty.

  • A lastNewPaintedAreaPaintTimingInfo which is a paint timing info, initially unset.

  • A lastNewPaintedAreaElement which is an Element or null, initially null.

  • A hasPendingChanges which is a boolean, initially false.

To create a Container Timing Record given a paint timing info paintTimingInfo and a DOMString identifier, perform the following steps:
  1. Let record be a new Container Timing Record.

  2. Set record’s paintTimingInfo to paintTimingInfo.

  3. Set record’s identifier to identifier.

  4. Return record.

6.4. Registering Container Roots

When an Element with a [^containertiming^] content attribute is connected to the document:

  1. The user agent must register the element as a container root.

  2. The user agent must initialize an empty painted region for the container root.

  3. The user agent must track all paint operations within the container root’s subtree, excluding any ignored subtrees.

6.5. Report Container Timing Updates

When asked to report a container paint given a Document document, a paint timing info paintTimingInfo, an ordered set of pending image records paintedImages, and an ordered set of elements paintedTextNodes, perform the following steps:

  1. For each record of paintedImages:

    1. Let imageElement be record’s element.

    2. If imageElement is not exposed for paint timing, given document, continue.

    3. If imageElement does not Contribute to container timing continue.

    4. Let intersectionRect be the value returned by the intersection rect algorithm using imageElement as the target and viewport as the root.

    5. Potentially add a ContainerTiming entry with paintTimingInfo, imageElement and intersectionRect.

  2. For each textNode of paintedTextNodes,

    1. If textNode is not exposed for paint timing, given document, continue.

    2. Let textNodeElement be the set of owned text nodes owning element.

    3. If textNodeElement does not Contribute to container timing continue.

    4. Let intersectionRect be an empty rectangle.

    5. For each Text node text of textNode’s set of owned text nodes:

      1. Augment intersectionRect to be smallest rectangle containing the border box of text and intersectionRect.

    6. Intersect intersectionRect with the visual viewport.

    7. Potentially add a ContainerTiming entry with paintTimingInfo, textNodeElement and intersectionRect.

6.6. Potentially add Container Timing Entries

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 the API.

In order to potentially add a ContainerTiming entry, the user agent must perform the following steps:

Input

intersectionRect, a DOMRectReadOnly

paintTimingInfo, a paint timing info

element, an Element

Output

None

  1. Let containerRoot be the result of get the container root element given element.

  2. If containerRoot is null, return.

  3. Let record be the entry in document’s container root records map for containerRoot. If no such entry exists, set record to the result of create a Container Timing Record given paintTimingInfo and the value of containerRoot’s [^containertiming^] content attribute; then add (containerRootrecord) to document’s container root records map.

6.7. Getting the parent container root Element

To get the parent container root element given an Element element, perform the following steps:
  1. Let parent be element’s parentElement.

  2. If parent is null, return null.

  3. Return the result of get the container root element given parent.

6.8. Contributes to Container Timing

An Element contributes to the container timing of a container root if all of the following are true:

To determine whether an Element element contributes to the container timing of a container root containerRoot:
  1. If element is null, return false.

  2. If element is in a shadow tree, return false.

  3. If element is not a descendant of containerRoot, return false.

  4. If element is within an ignored subtree, return false.

  5. Return true.

6.9. Getting the container root Element

To get the container root element given an Element element, perform the following steps:
  1. If element is null, return null.

  2. If element’s [^containertiming^] content attribute is present, return element.

  3. If element’s parentElement is not null, return the result of get the container root element given element’s parentElement.

  4. Return null.

6.10. Maybe Update Last New Painted Area

To maybe update the last new painted area for a Container Timing Record record given a Document document, a container root Element containerRoot, an Element element, a DOMRectReadOnly enclosingRect, and a paint timing info paintTimingInfo, perform the following steps:
  1. Let paintedRegion be record’s paintedRegion.

  2. If paintedRegion fully contains enclosingRect, return.

  3. Set record’s paintedRegion to the union of paintedRegion and enclosingRect.

  4. Set record’s lastNewPaintedAreaPaintTimingInfo to paintTimingInfo.

  5. Set record’s lastNewPaintedAreaElement to element.

  6. Set record’s hasPendingChanges to true.

  7. If containerRoot’s [^containertiming-ignore^] content attribute is present, return.

  8. Let parentContainerRoot be the result of get the parent container root element given containerRoot.

  9. If parentContainerRoot is null, return.

  10. Let parentRecord be the entry in document’s container root records map for parentContainerRoot. If no such entry exists, set parentRecord to the result of create a Container Timing Record given paintTimingInfo and the value of parentContainerRoot’s [^containertiming^] content attribute; then add (parentContainerRootparentRecord) to document’s container root records map.

  11. Maybe update the last new painted area for parentRecord given document, parentContainerRoot, element, enclosingRect, and paintTimingInfo.

6.11. Create a Container Timing Entry

In order to create a Container Timing entry given a Container Timing Record record, the user agent must perform the following steps:

Output

None

  1. Let entry be a new PerformanceContainerTiming entry with its:

  2. Queue the PerformanceEntry entry

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

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Conformant Algorithms

Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and abort these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm.

Conformance requirements phrased as algorithms or specific steps can be implemented in any manner, so long as the end result is equivalent. In particular, the algorithms defined in this specification are intended to be easy to understand and are not intended to be performant. Implementers are encouraged to optimize.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[GEOMETRY-1]
Sebastian Zartner; Yehonatan Daniv. Geometry Interfaces Module Level 1. URL: https://drafts.csswg.org/geometry/
[HR-TIME-3]
Yoav Weiss. High Resolution Time. URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[PAINT-TIMING]
Ian Clelland; Noam Rosenthal. Paint Timing. URL: https://w3c.github.io/paint-timing/
[PERFORMANCE-TIMELINE]
Nicolas Pena Moreno. Performance Timeline. URL: https://w3c.github.io/performance-timeline/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

IDL Index

[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;
    readonly attribute Element? rootElement;
};

PerformanceContainerTiming includes PaintTimingMixin;

partial interface Element {
    [CEReactions] attribute DOMString containertiming;
    [CEReactions] attribute DOMString? containertimingIgnore;
};