performance-observer.js – measure web performance metrics in your app (FCP, LCP, FID, CLS, TTFB + custom user, element, resource and navigation timings)

Generic interface for measuring performance metrics. It supports all web-vitals[1] and custom metrics[2]. Powered by … Read more performance-observer.js – measure web performance metrics in your app (FCP, LCP, FID, CLS, TTFB + custom user, element, resource and navigation timings)

Version
Coverage
License

Generic interface for measuring performance metrics. It supports all web-vitals[1] and custom metrics[2].
Powered by native browser PerformanceObserver API[3].

PerformanceObserver API[4] interface that can be used by developers to observe browser performance measurements like the speed of rendering of certain elements or responsiveness of user interactions on the page.

Unfortunately due to the early age of the API it lacks standardization and ease of use. It’s also quite low-level and for every metric you need to go through the documentation for implementing it separately and be aware of certain quirks and workarounds. For example, different types of metrics have different fields to get the performance value or require implementation of certain calculation formulas to get the meaningful metric result.

This projects aims to simplify the usage of PerformanceObserver API[5] for developers by encapsulating all the complexity, formulas and necessary workarounds. It provides few simple methods that will allow to subscribe to relevant performance metrics and retrieve them in a predictable format in order to send them later to the analytics service of your choice.

longtask[6]) you might want to include the script in the <head /> tag directly either by hardcoding it or loading from a CDN.

Here is an example of loading a library from a CDN using a classic script that sets the global performanceObserver object to window:

<script
  defer
  src="https://unpkg.com/@sumup/performance-observer@1.0.2/dist/performance-observer.es5.umd.min.js"
></script>
<script>
  window.performanceObserver.observe('first-input-delay', function (metric) {
    // report metric to your analytics system here
  });
</script>

And here’s an example of loading a library from a CDN using a module script (it’s safe to use module scripts in legacy browsers because unknown script types are ignored):

<script type="module">
  import performanceObserver from 'https://unpkg.com/@sumup/performance-observer@1.0.2/dist/performance-observer.es5.min.js?module';

  performanceObserver.observe('first-input-delay', function (metric) {
    // report metric to your analytics system here
  });
</script>

PerformanceObserver APIs[7] that are required to get metric values, are only available in Chromium-based browsers (e.g. Google Chrome, Microsoft Edge, Opera, Brave, Samsung Internet, etc.).

Browser support for each function is as follows:

  • first-paint – Chromium
  • first-contentful-paint – Chromium
  • largest-contentful-paint – Chromium
  • first-input-delay – Chromium
  • cumulative-layout-shift – Chromium
  • time-to-first-byte – Chromium, Firefox
  • user-timing – Chromium, Firefox
  • element-timing – Chromium
  • resource-timing – Chromium, Firefox
  • navigation-timing – Chromium, Firefox
  • longtask – Chromium

“First Paint” (FP)[8] returns a value in milliseconds that represents the time from when the browser navigation started (e.g. user clicks a link or hits enter after writing the URL in the browser navigation bar) until any first render is detected in the visible viewport. For example, painting the background colour on a body element could be regarded as “first-paint” in a web page’s load.

The observer callback is called once at the moment when the first paint happens on the page.

import performanceObserver from '@sumup/performance-observer';

performanceObserver.observe('first-paint',
  ({ name, value })) => {
    console.log(`"${name}": ${value}ms;`);
    // e.g. "first-paint": 1040ms;
  }
);

“First Contentful Paint” (FCP)[9] returns the value in milliseconds that represents the time from when the browser navigation started (e.g. user clicks a link or hits enter after writing the URL in the browser navigation bar) until the first content render is detected in the visible viewport. This could be elements containing text, image elements or canvas elements (though contents of iframe elements are not included).

The observer callback is called once at the moment when the first content element appears on the page.

import performanceObserver from '@sumup/performance-observer';

performanceObserver.observe('first-contentful-paint',
  ({ name, value })) => {
    console.log(`"${name}": ${value}ms;`);
    // e.g. "first-contentful-paint": 1041ms;
  }
);

“Largest Contentful Paint” (LCP)[10] returns the value in milliseconds that represents the time from when the browser navigation started (e.g. user clicks a link or hits enter after writing the URL in the browser navigation bar) until the largest (a.k.a main) content render is detected in the visible viewport. This could be elements containing large portion of text, image elements or video elements.

Due to the fact that web pages often load in stages, it’s possible that the largest element on the page might change. This means there could be several largest contentful paints on the page.

For example, if a page contains a block of text and a hero image, the browser may initially just render the text block and report a largest-contentful-paint, while later, once the hero image finishes loading, a second largest-contentful-paint metric would be reported.

In majority of cases we only want to know the most recent largest-contentful-paint that happened before the user started to interact with the page and that’s exactly when the observer callback is called by default. However, you can control its behavior using the third optional argument and force the observer to report metrics on every change.

import performanceObserver from '@sumup/performance-observer';

// this observer is called once on user interaction or
// when the tab is switched to another or closed completely...
performanceObserver.observe('largest-contentful-paint',
  ({ name, value })) => {
    console.log(`"${name}": ${value}ms;`);
    // e.g. "largest-contentful-paint": 1022ms;
  }
);

// ...and this observer is called every time when new metric appears
performanceObserver.observe(
  'largest-contentful-paint',
  ({ name, value })) => {
    console.log(`"${name}": ${value}ms;`);
    // e.g. "largest-contentful-paint": 1022ms;
  },
  true // report all changes
);

“First Input Delay” (FID)[11] returns the value in milliseconds that represents the time from when a user first interacts with the page (e.g. clicks on a link, taps on a button etc.) to the time when the browser is able to respond to that interaction.

The observer callback is called once at the moment when the first user interaction happens on the page.

import performanceObserver from '@sumup/performance-observer';

performanceObserver.observe('first-input-delay',
  ({ name, value })) => {
    console.log(`"${name}": ${value}ms;`);
    // e.g. "first-input-delay": 20ms;
  }
);

“Cumulative Layout Shift” (CLS)[12] returns the calculated value of the so-called “layout shift score”[13] which represents UI instability (e.g. elements position slightly changes throughout the page load time) in the visible viewport area of the page. However, layout shifts that occur within 500 milliseconds of user interaction are excluded from calculations, as they are considered as “user-initiated”.

Browsers report every change to CLS as a separate entry and this could happen very frequently, that’s why by default, the observer callback is called only once when the user closes the page or switches the tab. However, you can control the callback behavior using the third optional argument and force the observer to report CLS metrics on every change.

import performanceObserver from '@sumup/performance-observer';

// this observer is called once when the user switches the tab to another or closes the page...
performanceObserver.observe('cumulative-layout-shift',
  ({ name, value })) => {
    console.log(`"${name}": ${value};`);
    // e.g. "cumulative-layout-shift": 0.05;
  }
);

// ...and this observer is called every time when a new metric appears
performanceObserver.observe(
  'largest-contentful-paint',
  ({ name, value })) => {
    console.log(`"${name}": ${value};`);
    // e.g. "cumulative-layout-shift": 0.05;
  },
  true // report all changes toggle
);

“Time to First Byte” (TTFB)[14] returns the value in milliseconds that represents the time that a user’s browser took to receive the first byte of a page content from a server.

The metric is based on “Navigation Timing API”[15] and the observer callback is called only once at the moment when the page has completely loaded.

import performanceObserver from '@sumup/performance-observer';

performanceObserver.observe('time-to-first-byte',
  ({ name, value })) => {
    console.log(`"${name}": ${value}ms;`);
    // e.g. "time-to-first-byte": 120ms;
  }
);

“User Timing API”[16] measures the time in milliseconds that a certain block of code took to execute. It is useful for optimising complex logic and calculations on the page.

The observer callback is called once for each performance measure at the moment when you call window.performance.measure. Keep in mind that the callback can be called several times on the page if you’ve registered several measures in your code.

// start recording the time immediately before running a task
window.performance.mark('my-task:start');

await runMyTask();

// stop recording the time immediately after running a task
window.performance.mark('my-task:end');
window.performance.measure('my-task', 'my-task:start', 'my-task:end');
import performanceObserver from '@sumup/performance-observer';

performanceObserver.observe('user-timing',
  ({ name, value })) => {
    console.log(`"${name}": ${value}ms;`);
    // e.g. "my-task": 3022ms;
  }
);

“Element Timing API”[17] measures the time in milliseconds that a specific HTML element took to render on the screen. It can be useful for knowing when the largest image or text block was painted to the screen or if you want to measure the render time of some important element on the page.

The observer callback is called once per registered element at the moment when the element is rendered on the page. Keep in mind that the callback can be called several times on the page if you’ve registered several elements in HTML.

<img elementtiming="hero-image-paint" src="example.png" />
import performanceObserver from '@sumup/performance-observer';

performanceObserver.observe('element-timing',
  ({ name, value })) => {
    console.log(`"${name}": ${value}ms;`);
    // e.g. "hero-image-paint": 120ms;
  }
);

“Resource Timing API”[18] measures the time that a third-party resources of a page (e.g. images, styles, scripts, etc.) took to load.

The observer callback is called once per resource at the moment when the resource has been completely loaded by the browser. Keep in mind that the callback is called as many times as there are third-party resources loaded by the page.

import performanceObserver from '@sumup/performance-observer';

performanceObserver.observe('resource-timing',
  ({ name, meta, value })) => {
    console.log(`"${name}" (${meta.url}): ${value}ms;`);
    // e.g. "resource-timing" (http://sumup.com/favicon.ico): 20ms;
  }
);

“Navigation Timing API”[19] returns a value in milliseconds that represents the time that a page took to load completely. However, it can be also useful for understanding additional information[20] such as when the DOMContentLoaded and load events fire.

Please note that server response time, also known as “Time to First Byte”, was moved to a separate metric[21].

The observer callback is called once at the moment when the page has completely loaded.

import performanceObserver from '@sumup/performance-observer';

performanceObserver.observe('navigation-timing',
  ({ name, meta, value })) => {
    console.log(`"${name}" (${meta.url}): ${value}ms;`);
    // e.g. "navigation-timing" (http://sumup.com/products): 1352ms;
  }
);

“Long Tasks API”[22] returns a value in milliseconds that a particularly long task took to finish. It is useful for knowing when the browser’s main thread is blocked long enough to affect the frame rate or input latency. Currently, the API reports any tasks that executes for longer than 50 milliseconds.

The observer callback is called once per long task at the moment when this long task is completed by the browser. Keep in mind that the callback is called as many times as there are long tasks on the page.

⚠️ Important: you can track long tasks only by creating the observer in the <head> of your page before loading any other scripts. It’s needed because buffered flag is not currently supported for long tasks in any browser.

Long tasks are usually used for measuring Time to Interactive[23]. However, due to the complexity of calculations required to measure this metric we suggest to use a specialized script for that – https://github.com/GoogleChromeLabs/tti-polyfill[24].

import performanceObserver from '@sumup/performance-observer';

performanceObserver.observe('longtask',
  ({ name, value })) => {
    console.log(`"${name}": ${value}ms;`);
    // e.g. "longtask": 51ms;
  }
);

PeformanceObserver[25] if the metric exists, in other cases returns undefined and does nothing. A third optional argument is only relevant for certain metrics (e.g. largest-contentful-paint) and should be used with caution, only once you understand what you’re doing. See usage[26].

usage[27].

usage[28].

usage[29].

PeformanceObservers[30] registetered by the user. It can be useful if you need to access a particular observer in order to call some of its other native methods.

PeformanceObservers[31].

README.md[32] file.

“Measure and optimize performance and user experience”[33] documentation and is similar when it comes to web-vitals[34] (e.g. FCP, LCP, FID, CLS, TTFB) functionality to the corresponding package from Google – https://github.com/GoogleChrome/web-vitals[35] and is highly inspired by it.

read it and follow it[36].

If you feel another member of the community violated our CoC or you are experiencing problems participating in our community because of another individual’s behavior, please get in touch with our maintainers. We will enforce the CoC.

let us know or contribute some[37][38]

SumUp logo

It is our mission to make easy and fast card payments a reality across the entire world. You can pay with SumUp in more than 30 countries, already. Our engineers work in Berlin, Cologne, Sofia and Sāo Paulo. They write code in JavaScript, Swift, Ruby, Go, Java, Erlang, Elixir and more. Want to come work with us? Head to our careers page[39] to find out more.

References

  1. ^ web-vitals (web.dev)
  2. ^ custom metrics (web.dev)
  3. ^ PerformanceObserver API (developer.mozilla.org)
  4. ^ PerformanceObserver API (developer.mozilla.org)
  5. ^ PerformanceObserver API (developer.mozilla.org)
  6. ^ longtask (github.com)
  7. ^ PerformanceObserver APIs (developer.mozilla.org)
  8. ^ “First Paint” (FP) (developer.mozilla.org)
  9. ^ “First Contentful Paint” (FCP) (web.dev)
  10. ^ “Largest Contentful Paint” (LCP) (web.dev)
  11. ^ “First Input Delay” (FID) (web.dev)
  12. ^ “Cumulative Layout Shift” (CLS) (web.dev)
  13. ^ “layout shift score” (web.dev)
  14. ^ “Time to First Byte” (TTFB) (web.dev)
  15. ^ “Navigation Timing API” (github.com)
  16. ^ “User Timing API” (web.dev)
  17. ^ “Element Timing API” (web.dev)
  18. ^ “Resource Timing API” (web.dev)
  19. ^ “Navigation Timing API” (web.dev)
  20. ^ additional information (w3c.github.io)
  21. ^ separate metric (github.com)
  22. ^ “Long Tasks API” (w3c.github.io)
  23. ^ Time to Interactive (web.dev)
  24. ^ https://github.com/GoogleChromeLabs/tti-polyfill (github.com)
  25. ^ PeformanceObserver (developer.mozilla.org)
  26. ^ usage (github.com)
  27. ^ usage (github.com)
  28. ^ usage (github.com)
  29. ^ usage (github.com)
  30. ^ PeformanceObservers (developer.mozilla.org)
  31. ^ PeformanceObservers (developer.mozilla.org)
  32. ^ README.md (github.com)
  33. ^ “Measure and optimize performance and user experience” (web.dev)
  34. ^ web-vitals (web.dev)
  35. ^ https://github.com/GoogleChrome/web-vitals (github.com)
  36. ^ read it and follow it (github.com)
  37. ^ let us know (github.com)
  38. ^ contribute some (github.com)
  39. ^ Head to our careers page (sumup.com)


Source: Echo Js


Categories: Development, Software


Leave a Reply

Your email address will not be published. Required fields are marked *