Practical Chrome Devtools — Performance

Part 1: Practical Chrome Devtools — Common commands & Debugging

Chrome DevTools is a set of tools that can help you edit, measure and audit pages, diagnosing problems quickly, which ultimately helps you build better websites, faster. This is a summary of the most useful features of this powerful toolset that will help you in your daily basis work.

When running audits and/or performance profilings, keep in mind to always do that in an incognito browser. This is the best way to avoid external chrome extensions or specific browser configurations to affect the data.

Monitoring performance in real-time

A practical tool to check and measure the performance impacts in a page is the FPS meter, which provides real-time estimates for FPS as the page runs. This is useful info and it will help you to apply some changes in your page. To check that you should:

  • Press Cmd+Shift+p, type FPS and enable FPS Meter.
  • Use the page and check the FPS changes in real-time.

Layout thrashing and Paint flashing

Layout thrashing and paint flashing are the most common performance problem that happens in dynamic web applications (happening not only Progressive Web Apps and Single Page applications).

In summary, keep in mind that layout thrashing is related to reflow and paint flashing is about repainting.

If you want to know more about reflows and repaints, I recommend you to look at this post. It's old, but for sure is gold.

What forces layout / reflow

All of the below properties or methods, when requested/called in JavaScript, will trigger the browser to synchronously calculate the style and layout*. This is also called reflow or layout thrashing, and is common performance bottleneck.

Generally, all APIs that synchronously provide layout metrics will trigger forced reflow / layout. Read on for additional cases and details.

Element APIs

Getting box metrics
  • elem.offsetLeft, elem.offsetTop, elem.offsetWidth, elem.offsetHeight, elem.offsetParent
  • elem.clientLeft, elem.clientTop, elem.clientWidth, elem.clientHeight
  • elem.getClientRects(), elem.getBoundingClientRect()
Scroll stuff
  • elem.scrollBy(), elem.scrollTo()
  • elem.scrollIntoView(), elem.scrollIntoViewIfNeeded()
  • elem.scrollWidth, elem.scrollHeight
  • elem.scrollLeft, elem.scrollTop also, setting them
Setting focus
  • elem.computedRole, elem.computedName
  • elem.innerText (source)

Getting window dimensions

  • window.scrollX, window.scrollY
  • window.innerHeight, window.innerWidth
  • window.visualViewport.height / width / offsetTop / offsetLeft (source)


  • document.scrollingElement only forces style
  • document.elementFromPoint

Forms: Setting selection + focus

  • inputElem.focus()

Mouse events: Reading offset data

  • mouseEvt.layerX, mouseEvt.layerY, mouseEvt.offsetX, mouseEvt.offsetY (source)

Calling getComputedStyle()

window.getComputedStyle() will typically force style recalc.

window.getComputedStyle() will often force layout, as well.

Details of the conditions where gCS() forces layout

window.getComputedStyle() will force layout in one of 3 conditions:

  1. The element is in a shadow tree
  2. There are media queries (viewport-related ones). Specifically, one of the following: (source
    • min-width, min-height, max-width, max-height, width, height
    • aspect-ratio, min-aspect-ratio, max-aspect-ratio
    • device-pixel-ratio, resolution, orientation , min-device-pixel-ratio, max-device-pixel-ratio
  3. The property requested is one of the following: (source)
    • height, width
    • top, right, bottom, left
    • margin [-top, -right, -bottom, -left, or shorthand] only if the margin is fixed.
    • padding [-top, -right, -bottom, -left, or shorthand] only if the padding is fixed.
    • transform, transform-origin, perspective-origin
    • translate, rotate, scale
    • grid, grid-template, grid-template-columns, grid-template-rows
    • perspective-origin
    • These items were previously in the list but appear to not be any longer (as of Feb 2018): motion-path, motion-offset, motion-rotation, x, y, rx, ry

Getting Range dimensions

  • range.getClientRects(), range.getBoundingClientRect()


Quite a lot of properties/methods force, but I haven't made an exhaustive list. This list in incomplete:

  • SVGLocatable: computeCTM(), getBBox()
  • SVGTextContent: getCharNumAtPosition(), getComputedTextLength(), getEndPositionOfChar(), getExtentOfChar(), getNumberOfChars(), getRotationOfChar(), getStartPositionOfChar(), getSubStringLength(), selectSubString()
  • SVGUse: instanceRoot

Use the "chromium source tree link" below to explore on your own!


  • Lots & lots of stuff, …including copying an image to clipboard (source)

* Appendix

  • Reflow only has a cost if the document has changed and invalidated the style or layout. Typically, this is because the DOM was changed (classes modified, nodes added/removed, even adding a psuedo-class like :focus).
  • If layout is forced, style must be recalculated first. So forced layout triggers both operations. Their costs are very dependent on the content/situation, but typically both operations are similar in cost.
  • What should you do about all this? Well, the More on forced layout section below covers everything in more detail, but the short version is:
    1. for loops that force layout & change the DOM are the worst, avoid them.
    2. Use DevTools Performance Panel to see where this happens. You may be surprised to see how often your app code and library code hits this.
    3. Batch your writes & reads to the DOM (via FastDOM or a virtual DOM implementation). Read your metrics at the begininng of the frame (very very start of rAF, scroll handler, etc), when the numbers are still identical to the last time layout was done.

Timeline trace of The Guardian. Outbrain is forcing layout repeatedly, probably in a loop.

More on forced layout

Updated slightly April 2020. Codesearch links and a few changes to relevant element properties.

To see which CSS properties trigger layout or style recalcs, check out this resource:

Coverage, code splitting, and async imports

Let's think you have a big application loading everything in every page, even knowing that some of the loaded content is not necessary at that page. Or another scenario when you have a content which is used in your page, but it requires some user interaction to show that component.

These are clear scenarios when async load the content will be useful for everyone. To define/decide that you can use the Code Coverage feature.

  • cmd + shift + p
  • Choose Start instrumenting coverage and reload page option

When you chose this option your page will be reloaded and you can use simulate the user steps to visualize that content/feature. When finishing with the investigation, stop the record and check the coverage by.

  • cmd + shift + p
  • Choose Show Coverage option

This option will show the loaded files and the percentage used or not in these files based on your navigation, clicks and other events. It will give you a good idea of stylesheets, features, components, and modules you can import asynchronously. Applying these changes will help you to improve some metrics such as page load and page interaction

Check performance metrics via User timing API

What if you want to track a specific feature in your code? Let's say something like check how long a specific HTTP is taking from the request start to render the content on your page. User timings API for the rescue!

const fetchSomeData = async () => {
const measureName = 'fetchSomeData';
const startMark = `[START]: ${measureName}`;
const endMark = `[END]: ${measureName}`;
try {
const url = 'your-url';
// starting the User Timing API marker for this request
const data = await fetch(url);
return data;
} catch (error) {
console.error('Oops!', error);
} finally {
// Finishing the markers and sending the results
performance.measure(measureName, startMark, endMark);

You can check in the browser the time that took for this function to run and return the expected result. That will give you some insights about the reasons around the specific API request to be improved, cached for a long time or even avoided to be called multiple times.

HTTP Archive (HAR)

HAR (HTTP Archive) are files that can be generated by most browsers and contain a log of all HTTP requests and responses that happened during a certain period of time that was recording events in your webapp, they can also contain body content of API calls and file assets, making it excellent for debugging HTTP sessions.

Be conscious about your recordings. HAR files may contain sensitive information of the recorded pages, such as cookies, personal details and passwords.

You can record your HTTP session using the Network tab in the Developer Tools in Chrome.

  • Open the Developer Tools and click on the Network tab
  • Look for a round button at the top left of the Network tab. Check the box next to Preserve log and make sure it is red. If it is grey, click it once to start recording.
  • You can use the clear button (a circle with a diagonal line through it) right before trying to reproduce the issue to remove unnecessary header information
  • Reproduce the issue and save the capture by right-clicking on the grid and choosing Save as HAR with Content

Lighthouse Audits

Lighthouse is an open-source, automated tool for improving the quality of web pages. You can run it against any web page, public or requiring authentication. It has audits for performance, accessibility, progressive web apps, and more.

Start an audition is quite simple:

  • Open the Devtools and Click the Audits tab.
  • Click Perform an audit. DevTools shows you a list of audit categories. Enabled the relevant options for your audit and click Run audit and wait for the task finishes.

Lighthouse gives you a report on the page showing the scores of topics like performance and accessibility. Also, the report shares some pain points and approaches to solve these problems.


If you are tunning the same code in the Console repeatedly, consider saving the code in your Chrome Devtools as a Snippet instead. This will improve your workflow a lot.

You can run them by:

  • Opening your Chrome Devtools and type Cmd + Shift + p
  • Type !name-of-your-local-script

There are loads of different snippets if you do a quick search. I would recommend you to have a look in these 2 links:

Challenge yourself: Optimizing Website Speed

Feel free to do the Performance Codelab available on Chrome Developer website (

This tutorial has some of the most common issues in a website and it shows some approaches and techniques to improve them. All the solutions are based on the 80/20 approach, having mainly focus on 20% of the higher risks with huge wins and, after that, focusing on the 80%.


These are some of the features there are really useful to improve the performance of your web app. For sure there are lots of other features in Chrome Devtools that can be used to improve our web apps, resulting in better and faster pages, but these ones are the most relevant in my daily basis tasks.

What about you? It’s time to share what are you doing to improve your flow for performance checks.

Hope you enjoyed this reading. Thank you so much for reading until the end and see you soon!

Cya 👋


To keep up with posts on this blog, you can also subscribe via RSS.