Paint Timing API
The Paint Timing API exposes browser-recorded performance entries for important rendering milestones, notably `first-paint` and `first-contentful-paint`. Frontend engineers can read these timestamps through the Performance Timeline to understand when pixels and meaningful content first appeared.
The API is accessed through the browser's Performance Timeline. Calling `performance.getEntriesByType('paint')` returns `PerformancePaintTiming` entries representing supported paint milestones, with `name` identifying the milestone and `startTime` giving its timestamp relative to the page's performance time origin.
`first-paint` represents the browser's first recorded paint after navigation, while `first-contentful-paint` (FCP) marks when the browser first renders content such as text, an image, or another meaningful content element. FCP is generally more useful for assessing when users first see actual page content.
Paint timings are measurements rather than controls: reading them does not change rendering behavior. They can be collected in real-user monitoring (RUM) code and combined with other Performance APIs to investigate loading behavior.
Availability and exact reporting can vary by browser and execution context, so production telemetry should account for unsupported entries rather than assuming both milestones always exist. For ongoing observation of performance entries, the Performance Observer API is the more appropriate mechanism than repeatedly polling the timeline.
Key points
- `PerformancePaintTiming` entries describe browser paint milestones.
- `first-paint` records the first browser paint milestone when exposed.
- `first-contentful-paint` records the first paint of meaningful content.
- `startTime` is measured from the performance time origin.
- Use `PerformanceObserver` for observing performance entries as they become available.
Interview questions
What does the Paint Timing API measure?
It exposes browser performance entries for early rendering milestones. The commonly discussed entries are `first-paint` and `first-contentful-paint`. Each entry has a name and a timing value such as `startTime`. These measurements can be used to understand how quickly users begin seeing the page.
How would you read the page's paint timings?
I would call `performance.getEntriesByType('paint')` and inspect the returned `PerformancePaintTiming` objects. The `name` property identifies the milestone, and `startTime` gives its timing relative to the performance time origin. I would check that an entry exists before using it because browser support and reporting can vary. For production monitoring, I would generally consider `PerformanceObserver` when I need to observe entries as they become available.
What is the difference between First Paint and First Contentful Paint?
First Paint indicates the browser's first recorded paint milestone. First Contentful Paint is more specific: it records the first rendering of qualifying content, such as text or an image. Therefore, FCP gives a stronger indication of when users first see actual page content. Neither metric describes the complete loading experience.
Why shouldn't a frontend application assume that paint entries always exist?
Performance APIs are implemented with browser and context differences, so an application should not blindly assume a particular entry is present. `getEntriesByType('paint')` can return no matching entries, and code should handle that case safely. This is especially important for telemetry because a missing measurement should not break application behavior. Production monitoring should record availability separately from the measured value.
How would you use paint timing in real-user monitoring?
I would collect supported paint entries from real users and associate the measurements with relevant page-load context. I would avoid interpreting a single measurement as representative of all users and instead analyze distributions across appropriate populations and environments. Paint metrics can then be compared with other browser performance measurements to identify loading bottlenecks. The collection code should be lightweight and resilient to missing entries or unsupported features.