A directory of the performance timeline
The browser records timing information as a stream of PerformanceEntry objects,
each tagged with an entryType. Knowing which types exist, which interface each
produces, and whether to read them with getEntriesByType() or a
PerformanceObserver is the key to measuring real-user performance and the Core
Web Vitals. This searchable reference lists them all.
How it works
Some entry types are buffered and read directly; the modern vitals are delivered through an observer:
// Buffered: available after load
const nav = performance.getEntriesByType("navigation")[0];
console.log(nav.loadEventEnd - nav.startTime);
// Observer: Core Web Vitals and long tasks
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(entry.entryType, entry.startTime, entry);
}
}).observe({ type: "largest-contentful-paint", buffered: true });
Your own code can add mark and measure entries with performance.mark() and
performance.measure(). Newer types — longtask, layout-shift,
largest-contentful-paint, event — must be observed, and buffered: true
recovers entries that fired before the observer was attached.
Entry types mapped to Core Web Vitals
| Core Web Vital | Entry type | Key property |
|---|---|---|
| LCP (Largest Contentful Paint) | largest-contentful-paint | startTime, size, element |
| CLS (Cumulative Layout Shift) | layout-shift | value, hadRecentInput |
| INP (Interaction to Next Paint) | event | duration, processingStart |
| FID (deprecated) | first-input | processingStart - startTime |
For LCP and CLS, always observe with buffered: true — these events often fire before your observer is attached on fast-loading pages.
Buffered vs observer-only: a practical decision tree
Use performance.getEntriesByType() when you only need to query after load and the type is buffered: navigation, resource, paint, mark, measure. These are already in the buffer by the time your script runs after DOMContentLoaded or load.
Use PerformanceObserver when the type is observer-only (longtask, layout-shift, largest-contentful-paint, event, first-input), or when you need to react to entries as they arrive rather than reading a snapshot. Attach observers early in your script — before the page fully renders — to capture events that happen during initial load.
Practical patterns
// Measure your own code
performance.mark("fetchStart");
await fetchData();
performance.mark("fetchEnd");
performance.measure("fetchDuration", "fetchStart", "fetchEnd");
const [entry] = performance.getEntriesByName("fetchDuration");
console.log(entry.duration); // milliseconds
// Detect heavy resources
const slow = performance.getEntriesByType("resource")
.filter(r => r.duration > 500);
Tips and notes
- Use
mark/measureto instrument your own code, then read them back as entries. - Always observe vitals with
buffered: trueso you do not miss the earliest LCP or layout shifts. resourceentries exposetransferSizeandinitiatorTypefor spotting heavy or slow assets.- A
longtaskover 50 ms is a yellow flag for input responsiveness — investigate its attribution. layout-shiftentries withhadRecentInput: trueshould be excluded from CLS calculations, per the Web Vitals spec, because shifts triggered by user interaction do not count against the score.