The essentials
Quick reference
One focused task per row. Jump to the related section for complete, working examples.
| Use | Syntax | Examples |
|---|---|---|
| Detect observable types | const types = PerformanceObserver.supportedEntryTypes | View examples |
| Observe buffered entries | observer.observe({ type: 'largest-contentful-paint', buffered: true }) | View examples |
| Drain queued records | const pending = observer.takeRecords() | View examples |
| Stop observation | observer.disconnect() | View examples |
| Read navigation timing | const [nav] = performance.getEntriesByType('navigation') | View examples |
| Compute response wait | const ttfb = nav.responseStart - nav.requestStart | View examples |
| Read resource timings | const resources = performance.getEntriesByType('resource') | View examples |
| Expose cross-origin detail | Timing-Allow-Origin: https://app.example | View examples |
| Clear the resource buffer | performance.clearResourceTimings() | View examples |
| Mark a workflow point | performance.mark('checkout:start') | View examples |
| Measure between marks | performance.measure('checkout', 'checkout:start', 'checkout:end') | View examples |
| Clear application marks | performance.clearMarks('checkout:start') | View examples |
| Read first contentful paint | performance.getEntriesByName('first-contentful-paint', 'paint')[0] | View examples |
| Observe long interactions | observer.observe({ type: 'event', buffered: true, durationThreshold: 40 }) | View examples |
| Bound telemetry payloads | navigator.sendBeacon('/rum', new Blob([payload], { type: 'application/json' })) | View examples |
The Performance Timeline records high-resolution entries for navigation, resources, application marks, rendering, and selected user interactions. PerformanceObserver delivers supported entry types without polling. Measure real user journeys with a bounded schema, observe buffered entries where defined, avoid deprecated timing interfaces, and treat URLs and fine-grained timestamps as potentially sensitive telemetry.
Step by step
Detailed examples
Register narrowly scoped observers and drain them on teardown
PerformanceObserver callbacks receive batches asynchronously. Check supportedEntryTypes because entry support differs by browser and global. The type form accepts buffered but observes one type; entryTypes observes several types but cannot be combined with type, buffered, or durationThreshold. Subscribe only to useful types, process records quickly, use takeRecords() before disconnect when final queued data matters, and cap in-memory aggregation.
const observers = [];
function observe(type, callback, options = {}) {
if (!PerformanceObserver.supportedEntryTypes.includes(type)) return;
const observer = new PerformanceObserver(list => callback(list.getEntries()));
observer.observe({ type, ...options });
observers.push(observer);
}
observe('longtask', reportLongTasks, { buffered: true });
addEventListener('pagehide', () => observers.forEach(observer => observer.disconnect()), { once: true }); Interpret resource timing with cross-origin restrictions
PerformanceResourceTiming covers fetched resources, including initiator type, protocol, transfer sizes, and fetch phases. For most cross-origin resources, detailed timestamps and sizes are zero unless the response opts in with Timing-Allow-Origin; CORS alone does not expose timing. Cached responses complicate transferSize interpretation. The resource buffer is finite, so observe entries or handle resourcetimingbufferfull, resize deliberately, then clear records in long-lived applications.
const rows = performance.getEntriesByType('resource').map(entry => ({
url: new URL(entry.name).pathname,
kind: entry.initiatorType,
duration: Math.round(entry.duration),
transferred: entry.transferSize,
protocol: entry.nextHopProtocol,
}));
performance.clearResourceTimings(); Instrument user journeys with bounded names and details
performance.mark() and measure() create PerformanceMark and PerformanceMeasure entries that observers can consume. Use a controlled vocabulary rather than embedding account IDs, search strings, or arbitrary URLs in names. Modern mark and measure options can specify startTime, detail, start, end, or duration, but feature support should be checked. Clear marks and measures after reporting; these APIs measure client spans and do not replace server tracing.
async function measuredCheckout() {
performance.mark('checkout:start');
try { return await submitCheckout(); }
finally {
performance.mark('checkout:end');
performance.measure('checkout', 'checkout:start', 'checkout:end');
performance.clearMarks('checkout:start');
performance.clearMarks('checkout:end');
}
} Observe rendering and responsiveness with metric-specific rules
Paint Timing exposes first-paint and first-contentful-paint. Largest Contentful Paint entries are reported as candidates until user interaction or page hiding; layout-shift entries must be grouped into session windows and shifts with recent input excluded for CLS. Event Timing and Long Animation Frames support responsiveness diagnosis but differ in availability and semantics. Do not invent Core Web Vitals calculations from one raw entry—use the published algorithms or a maintained library and test back-forward cache restores.
let lcp;
if (PerformanceObserver.supportedEntryTypes.includes('largest-contentful-paint')) {
const observer = new PerformanceObserver(list => {
lcp = list.getEntries().at(-1) ?? lcp;
});
observer.observe({ type: 'largest-contentful-paint', buffered: true });
addEventListener('pagehide', () => {
lcp = observer.takeRecords().at(-1) ?? lcp;
observer.disconnect();
reportLcp(lcp);
}, { once: true });
} Minimize telemetry and preserve measurement context
Timing precision, resource URLs, element identifiers, and interaction targets may reveal browsing behavior or personal data. Strip query strings, use allowlisted metric names, sample, aggregate, and apply retention controls. Cross-origin isolation can increase timer precision and therefore risk. Use sendBeacon() or keepalive fetch only for a small final payload, never assume delivery, and do not block unloading. Include navigation type, visibility, device class, and version only when needed to interpret results.
function report(metrics) {
const payload = JSON.stringify({
page: location.pathname,
build: APP_BUILD,
metrics: Object.fromEntries(Object.entries(metrics).filter(([, value]) => Number.isFinite(value))),
});
if (payload.length < 32000) navigator.sendBeacon('/rum', new Blob([payload], { type: 'application/json' }));
} Sources and further reading
References
Authoritative documentation used to verify and expand this cheat sheet.
- World Wide Web ConsortiumPerformance Timelinew3.org
- World Wide Web ConsortiumNavigation Timing Level 2w3.org
- World Wide Web ConsortiumResource Timingw3.org
- World Wide Web ConsortiumUser Timing Level 3w3.org
- World Wide Web ConsortiumPaint Timing Level 1w3.org
- World Wide Web ConsortiumEvent Timingw3.org
Help us improve
Found a typo or missing example?
Tell us what would make this cheat sheet clearer, more complete, or more useful.



