Mastering Front-End Performance: How to Use PerformanceObserver & Metrics

This article explains how to monitor and analyze front‑end performance using the deprecated performance.timing API and the modern PerformanceObserver, detailing key web‑vital metrics such as TTFB, FCP, LCP, FID, and CLS, with code examples and practical interpretation guidelines.

SQB Blog
SQB Blog
SQB Blog
Mastering Front-End Performance: How to Use PerformanceObserver & Metrics

Web technologies are widely used across iOS, Android, PC, Mac, Linux and other platforms, making performance monitoring essential to identify bottlenecks and optimize page speed.

performance.timing

performance.timing returns a PerformanceTiming object containing read‑only millisecond timestamps for each stage of page loading. const timing = performance.timing; connectEnd

connectStart

domComplete

secureConnectionStart

unloadEventEnd

unloadEventStart

By chaining these timestamps you can compute common performance metrics.

Examples of metric calculations:

White screen time

const general = timing.domComplete - timing.navigationStart;

Redirect time

const redirect = timing.fetchStart - timing.navigationStart;

DNS lookup time

const dns = timing.domainLookupEnd - timing.domainLookupStart;

TCP connection time

const connect_tcp = timing.connectEnd - timing.connectStart;

Request time

const request = timing.responseStart - timing.requestStart;

Response download time

const response = timing.responseEnd - timing.responseStart;

Asset download time

const assets = timing.domComplete - timing.responseEnd;

Note: performance.timing is deprecated.

Deprecated: This feature is no longer recommended. Some browsers still support it, but it may be removed from the web standard.

It only measures network‑related data and cannot capture detailed user‑interaction timings.

PerformanceObserver

PerformanceObserver can listen to PerformanceEntry events such as marks, measures, paints, largest‑contentful‑paint, etc.

const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach((entry) => {
    console.log(`name: ${entry.name}, type: ${entry.entryType}, start: ${entry.startTime}, duration: ${entry.duration}`);
  });
});
observer.observe({ entryTypes: ["mark", "measure"] });
performance.mark("registered-observer");

Browser output example:

name: registered-observer, type: mark, start: 4254.199999999255, duration: 0
name: routeChange, type: mark, start: 6358.5999999996275, duration: 0
name: beforeRender, type: mark, start: 6406, duration: 0
name: Next.js-route-change-to-render, type: measure, start: 6358.5999999996275, duration: 47.40000000037253
name: afterRender, type: mark, start: 6409.0999999996275, duration: 0

PerformanceEntry.entryTypes include:

element – reports element load time (string)

navigation – page URL (URL)

resource – resource URL (URL)

mark – name passed to performance.mark() (string)

measure – name passed to performance.measure() (string)

paint – "first-paint" or "first-contentful-paint"

longtask – reports long‑task instances (string)

TTFB

TTFB: Time to First Byte – time from request start to first byte received.

Collect TTFB with PerformanceObserver on navigation entries:

const observer = new PerformanceObserver((list) => {
  const [pageNav] = list.getEntriesByType("navigation");
  console.log(`TTFB: ${pageNav.responseStart}`);
});
observer.observe({ entryTypes: ["navigation"] });

Good: < 800 ms; Poor: > 1800 ms.

FCP

FCP: First Contentful Paint – time until the first text or image is rendered.

Collect FCP with PerformanceObserver on "paint" entries:

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    const metricName = entry.name;
    const time = Math.round(entry.startTime + entry.duration);
    console.log(metricName, time);
  }
});
observer.observe({ entryTypes: ["paint"] });

Example output: first-paint 1139, first-contentful-paint 1139.

Excellent: < 1.8 s; Average: 1.8‑3 s; Poor: > 3 s.

LCP

LCP: Largest Contentful Paint – time when the largest visible element is rendered.

Collect LCP with PerformanceObserver on "largest-contentful-paint":

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log("LCP: ", entry.startTime);
  }
});
observer.observe({ entryTypes: ["largest-contentful-paint"] });

Example output: LCP: 99.099.

Excellent: < 2.5 s; Poor: > 4 s.

FID

FID: First Input Delay – latency between user interaction and browser response.

Collect FID with PerformanceObserver on "first-input":

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    const time = entry.processingStart - entry.startTime;
    console.log(entry.entryType, time);
  }
});
observer.observe({ entryTypes: ["first-input"] });
function onClick() { console.log("click"); }

Example output: first-input 4.2.

Target: < 100 ms.

CLS

CLS: Cumulative Layout Shift – measures visual stability of the page.

Calculate CLS using "layout-shift" entries:

let clsValue = 0;
let clsEntries = [];
let sessionValue = 0;
let sessionEntries = [];
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    if (!entry.hadRecentInput) {
      const firstSessionEntry = sessionEntries[0];
      const lastSessionEntry = sessionEntries[sessionEntries.length - 1];
      if (sessionValue && entry.startTime - lastSessionEntry.startTime < 1000 && entry.startTime - firstSessionEntry.startTime < 5000) {
        sessionValue += entry.value;
        sessionEntries.push(entry);
      } else {
        sessionValue = entry.value;
        sessionEntries = [entry];
      }
      if (sessionValue > clsValue) {
        clsValue = sessionValue;
        clsEntries = sessionEntries;
        console.log("CLS:", clsValue, clsEntries);
      }
    }
  }
}).observe({ type: "layout-shift", buffered: true });
setTimeout(() => {
  document.querySelector("div").style.marginTop = "25vh";
}, 1000);

CLS < 0.1 is good; > 0.25 is poor.

Conclusion

The article demonstrates how to use the performance and PerformanceObserver APIs to monitor front‑end metrics such as server response, first paint, largest contentful paint, input delay, and layout shift, and explains how to interpret the values for optimization.

References: https://github.com/WICG/layout-instability

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

frontendMetricsweb-vitalsperformanceobserver
SQB Blog
Written by

SQB Blog

Thank you all.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.