Now Reading:

Measuring In Browser User Experience With Performance API

Ever felt like your beautifully crafted application was running slower than a sloth in a sunbeam; but you couldn’t quite prove it?

You are not alone my friend. Frontend performance can be slippery to measure and even trickier to explain to non-technical teammates. Enter the Performance API your built-in browser friend that helps you measure how fast (or not) your app is running in the wild, from the perspective of real users.

What is the Performance API?

#

At its core, the Performance API is a set of tools exposed by modern browsers that help you measure the timing of events in your web application from page loads to custom code executions.

Think of it as a stopwatch for your site, letting you measure things like, understanding page loading time, hen did the user actually see content and find out how long did a particular user interaction take to process.

The Performance API provides a standardised way to get accurate timings, right from the browser, no extra dependencies required- yay!

Despite what you may have heard. Performance isn't just about numbers, it’s about user experience. Slow pages drive users away. But with performance measurements, you’re not just guessing. You're making data-informed decisions to improve your app.

Without following best practices, it’s easy for your code to be deceptively expensive and users expect apps to feel fast, not just look polished (although that can help).

The Core Parts of the Performance API

#

Measuring Performance Timing

#

performance.now() gives you a high-resolution timestamp in milliseconds**,** relative to the page load.


_10
const start = performance.now();
_10
_10
// Do something expensive...
_10
for (let i = 0; i < 1e6; i++) {}
_10
_10
const end = performance.now();
_10
console.log(`Loop took ${end - start}ms`);

Which is super handy for measuring the execution time of functions or user interactions.

Labelling Performance Timing Measurements

#

performance.mark('startFetch') lets you define named markers in your code and measure the time between them.


_11
performance.mark('startFetch');
_11
_11
fetch('/api/dogs')
_11
.then(res => res.json())
_11
.then(data => {
_11
performance.mark('endFetch');
_11
performance.measure('fetchDuration', 'startFetch', 'endFetch');
_11
_11
const measure = performance.getEntriesByName('fetchDuration')[0];
_11
console.log(`API call took ${measure.duration}ms`);
_11
});

This is great for tracking custom performance events — like API calls, React re-renders, or third-party script loads.

Getting Performance Entries By Type

performance.getEntriesByType() allows you to get built-in performance timings from the browser.


_10
const resources = performance.getEntriesByType('resource');
_10
_10
resources.forEach(entry => {
_10
console.log(`${entry.name} loaded in ${entry.duration}ms`);
_10
});

Perfect for auditing how your fonts, images, or scripts are performing.

Using the Performance Buffer

#

When working with the Performance API, it’s important to understand that all your marks, measures, and other performance entries are stored in something called the performance buffer; think of it like a temporary, in-memory log of everything you're measuring.

The buffer holds different types of entries, marks, measures, resource timings, and amongs other things and you can access them using performance.getEntriesByType() or performance.getEntriesByName().

For example:


_10
const measures = performance.getEntriesByType('measure');
_10
measures.forEach((entry) => {
_10
console.log(`${entry.name}: ${entry.duration}ms`);
_10
});

However, the performance buffer is not infinite. Most browsers limit the number of stored entries. Chrome, for example, limits it to 150 entries per type.

Once that limit is hit, older entries are silently dropped, which can make debugging tricky if you're not aware of it.

To manage the buffer effectively you can use performance.clearMarks() and performance.clearMeasures() to remove entries you no longer need.

Cleaning Up Performance Buffer

#

Here’s a practical cleanup example:


_10
useEffect(() => {
_10
performance.mark('DogModal-open');
_10
_10
return () => {
_10
performance.mark('DogModal-close');
_10
performance.measure('DogModal-visible', 'DogModal-open', 'DogModal-close');
_10
performance.clearMarks('DogModal-open');
_10
performance.clearMarks('DogModal-close');
_10
};
_10
}, []);

This ensures your measurements are accurate and your buffer stays tidy, a small step that goes a long way in keeping your performance data useful and your debugging experience smooth.

Monitoring Performance Buffer Updates

#

What if you want to react to new performance entries in real time? That’s where PerformanceObserver comes in. This handy API lets you subscribe to the performance buffer and get notified whenever new entries are added, such as marks, measures, or even long tasks.

The PerformanceObserver works a bit like MutationObserver or IntersectionObserver. You create an instance, specify the types of entries you want to observe, and provide a callback to handle them.

Here’s a basic setup:


_10
const observer = new PerformanceObserver((list) => {
_10
const entries = list.getEntries();
_10
entries.forEach((entry) => {
_10
console.log(`[${entry.entryType}] ${entry.name}: ${entry.duration?.toFixed(2)}ms`);
_10
});
_10
});
_10
_10
observer.observe({ entryTypes: ['measure', 'mark'] });

This observer listens for any new measure or mark entries and logs them immediately when they're added to the buffer.

Isolate Long Running Tasks

You can observe 'longtask' entries to identify jank and responsiveness issues.


_10
observer.observe({ entryTypes: ['longtask'] });

Long tasks are anything that blocks the main thread for over 50ms, super useful for diagnosing those "the UI feels stuck" moments.

Don’t Forget to Disconnect

Like any good observer, you should clean up when you're don


_10
observer.disconnect();

Especially in SPA environments or during tests, disconnecting prevents memory leaks and keeps your app running efficiently.

Using the Performance API in a React App

#

Let’s say you want to measure how long it takes for a component to mount and render. Here's how you could do it with React hooks.


_17
import React, { useEffect } from 'react';
_17
_17
export function DogGallery() {
_17
useEffect(() => {
_17
performance.mark('DogGallery-start');
_17
_17
return () => {
_17
performance.mark('DogGallery-end');
_17
performance.measure('DogGallery-mount', 'DogGallery-start', 'DogGallery-end');
_17
_17
const measurement = performance.getEntriesByName('DogGallery-mount')[0];
_17
console.log(`DogGallery mounted in ${measurement.duration}ms`);
_17
};
_17
}, []);
_17
_17
return <div>{/* your dog pictures here */}</div>;
_17
}

Stay ahead of the pack 🐶

Join the newsletter to get the latest articles and expert advice directly to your inbox.
Totally free, no spam and you can unsubscribe anytime you want!

  • Expert Tips
  • No Spam
  • Latest Updates

I'll never share any of your information with a third party. That's a promise.

Diagnosing Slow Interactions

#

Let’s say you’ve got a button that loads a modal, and users are reporting a delay. Instead of guessing, you can use performance.mark()


_13
function openModal() {
_13
performance.mark('openModal-start');
_13
_13
setShowModal(true); // triggers modal UI to appear
_13
_13
requestAnimationFrame(() => {
_13
performance.mark('openModal-end');
_13
performance.measure('openModal', 'openModal-start', 'openModal-end');
_13
_13
const measure = performance.getEntriesByName('openModal')[0];
_13
console.log(`Modal appeared in ${measure.duration.toFixed(2)}ms`);
_13
});
_13
}

Using requestAnimationFrame() here ensures you're measuring after the next paint, giving you a clearer picture of perceived performance.

Tips For Using Performance API Effectively

#

Using the Performance API effectively comes down to being intentional with what and how you measure. Start by labelling your marks clearly. For example, names like "data-fetch-start" or "dog-gallery-render" will save you loads of debugging time later.

Be mindful to measure only what matters; tracking every single render or minor event can create unnecessary noise and make it harder to focus on real performance issues.

It’s also a good idea to use these measurements in both development and production, but be sure to strip or filter logs in production environments so you’re not cluttering the console or leaking internal details.

Finally, for a more complete view of your app’s performance, combine the Performance API with user-centric tools like Web Vitals which this gives you insight not just into what your app is doing, but how it feels to the user.

Use the Performance API to get real, actionable insights into what your users are experiencing. It’s a core part of building user-friendly, delightful frontend experiences.

Whether you’re debugging a janky animation or optimising API calls, measuring is the first step to improving.

Conclusion

#

The Performance API is one of the most underutilised tools in the frontend toolbox, but once you know how to use it, it becomes a powerful ally in building faster, smoother applications.

Understanding how your app feels to your users and improving that with real data is one of the most crucial impacts you can have as frontend engineer.

Keep on coding in the free world,

DANNY

Reinforce Your Learning

One of the best ways to reinforce what your learning is to test yourself to solidify the knowlege in your memory.
Complete this 7 question quiz to see how much you remember.

1) What does the Performance API primarily help frontend developers do?
file under:
Performance

Thanks alot for your feedback!

The insights you share really help me with improving the quality of the content here.

If there's anything you would like to add, please send a message to:

[email protected]

Was this article this helpful?

D is for danny

About the author

Danny Engineering

A software engineer with a strong belief in human-centric design and driven by a deep empathy for users. Combining the latest technology with human values to build a better, more connected world.

Gobacktothetop

Made with 🥰 in 🏴󠁧󠁢󠁥󠁮󠁧󠁿

©2025 All rights reserved.