PerformanceObserver

A browser listener that receives paint, resource, layout-shift, interaction, and long-task measurements as performance entries appear.

listen for browser performance eventscollect web vitals without pollingwatch layout shifts in JavaScriptget paint timings as they happenbrowser performance event listenerhow does the web-vitals library actually workknow when the page stutters for real usersperformance observor

See it

Live demo coming soon

What it is

PerformanceObserver is the browser's subscription API for the Performance Timeline. You tell it which entry type to watch, such as paint, resource, largest-contentful-paint, layout-shift, event, or longtask, and it delivers matching entries to a callback in batches. Using 'buffered: true' can also include entries recorded before the observer was attached.

Reach for it when building real-user monitoring or a focused diagnostic that must keep collecting after page load. It avoids repeatedly polling performance.getEntries(), and one callback can filter, aggregate, and queue small telemetry records for delivery. Google's web-vitals library is a thin, carefully edge-cased wrapper over exactly this API.

Gotcha: entry-type support differs by browser, so check PerformanceObserver.supportedEntryTypes before observing. The 'buffered' option works with the single 'type' form of observe(), not the 'entryTypes' array form. Keep callbacks cheap, because heavy measurement code can create the performance problem it is recording.

Ask AI for it

Build a lightweight real-user collector with PerformanceObserver. Feature-detect each requested type through PerformanceObserver.supportedEntryTypes, then create a separate observer using observe({ type, buffered: true }) for supported paint, largest-contentful-paint, layout-shift, event, longtask, and resource entries. Ignore layout-shift entries where hadRecentInput is true, group PerformanceEventTiming records by interactionId, and keep only the fields needed for LCP, CLS, INP, long-task, and resource reports. Batch records in memory, flush them with navigator.sendBeacon on visibilitychange, disconnect every observer after flushing, and make the collector silently skip unsupported entry types.

You might have meant

real user monitoringlargest contentful paintinteraction to next paintcumulative layout shiftlong task

Go deeper