Oops! Something went wrong while submitting the form.
We use cookies to improve your browsing experience on our website, to show you personalised content and to analize our website traffic. By browsing our website, you consent to our use of cookies. Read privacy policy.
An app is only as good as the problem it solves. But your app's performance can be extremely critical to its success as well. A slow-loading web app can make users quit and try out an alternative in no time. Testing an app’s performance should thus be an integral part of your development process and not an afterthought.
In this article, we will talk about how you can proactively monitor and boost your app’s performance as well as fix common issues that are slowing down the performance of your app.
I’ll use the following tools for this blog.
Lighthouse - A performance audit tool, developed by Google
Webpack - A JavaScript bundler
You can find similar tools online, both free and paid. So let’s give our Vuea new Angularperspective to make our apps Reactfaster.
Performance Metrics
First, we need to understand which metrics play an important role in determining an app’s performance. Lighthouse helps us calculate a score based on a weighted average of the below metrics:
First Contentful Paint (FCP) - 15%
Speed Index (SI) - 15%
Largest Contentful Paint (LCP) - 25%
Time to Interactive (TTI) - 15%
Total Blocking Time (TBT) - 25%
Cumulative Layout Shift (CLS) - 5%
By taking the above stats into account, Lighthouse gauges your app's performance as such:
0 to 49 (slow): Red
50 to 89 (moderate): Orange
90 to 100 (fast): Green
I would recommend going through Lighthouse performance scoring to learn more. Once you understand Lighthouse, you can audit websites of your choosing.
I gathered audit scores for a few websites, including Walmart, Zomato, Reddit, and British Airways. Almost all of them had a performance score below 30. A few even secured a single-digit.
To attract more customers, businesses fill their apps with many attractive features. But they ignore the most important thing: performance, which degrades with the addition of each such feature.
As I said earlier, it’s all about the user experience. You can read more about why performance matters and how it impacts the overall experience.
Now, with that being said, I want to challenge you to conduct a performance test on your favorite app. Let me know if it receives a good score. If not, then don’t feel bad.
Below your scores are the possible opportunities suggested by Lighthouse. Fixing these affects the performance metrics above and eventually boosts your app's performance. So let’s check them out one-by-one.
Here are all the possible opportunities listed by Lighthouse:
Eliminate render-blocking resources
Properly size images
Defer offscreen images
Minify CSS & JavaScript
Serve images in the next-gen formats
Enable text compression
Preconnect to required origins
Avoid multiple page redirects
Use video formats for animated content
A few other opportunities won't be covered in this blog, but they are just an extension of the above points. Feel free to read them under the further reading section.
This section lists down all the render-blocking resources. The main goal is to reduce their impact by:
removing unnecessary resources,
deferring non-critical resources, and
in-lining critical resources.
To do that, we need to understand what a render-blocking resource is.
Render-blocking resource and how to identify
As the name suggests, it’s a resource that prevents a browser from rendering processed content. Lighthouse identifies the following as render-blocking resources:
A <script> </script>tag in <head> </head>that doesn’t have a defer or async attribute
A <link rel="”stylesheet”"> tag that doesn’t have a media attribute to match a user's device or a disabled attribute to hint browser to not download if unnecessary
A <link rel="”import”"> that doesn’t have an async attribute
To reduce the impact, you need to identify what’s critical and what’s not. You can read how to identify critical resources using the Chrome dev tool.
Classify Resources
Classify resources as critical and non-critical based on the following color code:
Green (critical): Needed for the first paint.
Red (non-critical): Not needed for the first paint but will be needed later.
Solution
Now, to eliminate render-blocking resources:
Extract the critical part into an inline resource and add the correct attributes to the non-critical resources. These attributes will indicate to the browser what to download asynchronously. This can be done manually or by using a JS bundler.
Webpack users can use the libraries below to do it in a few easy steps:
For extracting critical CSS, you can use html-critical-webpack-plugin or critters-webpack-plugin. It’ll generate an inline <style></style> tag in the <head></head> with critical CSS stripped out of the main CSS chunk and preloading the main file
For extracting CSS depending on media queries, use media-query-splitting-plugin or media-query-plugin
The first paint doesn't need to be dependent on the JavaScript files. Use lazy loading and code splitting techniques to achieve lazy loadingresources (downloading only when requested by the browser). The magic comments in lazy loadingmake it easy
And finally, for the mainchunk, vendor chunk, or any other external scripts (included in index.html), you can defer them using script-ext-html-webpack-plugin
There are many more libraries for inlining CSS and deferring external scripts. Feel free to use as per the use case.
Use Properly Sized Images
This section lists all the images used in a page that aren’t properly sized, along with the stats on potential savings for each image.
How Lighthouse Calculate Oversized Images?
Lighthouse calculates potential savings by comparing the rendered size of each image on the page with its actual size. The rendered image varies based on the device pixel ratio. If the size difference is at least 25 KB, the image will fail the audit.
Solution
DO NOT serve images that are larger than their rendered versions! The wasted size just hampers the load time.
Alternatively,
Use responsive images. With this technique, create multiple versions of the images to be used in the application and serve them depending on the media queries, viewport dimensions, etc
Use image CDNs to optimize images. These are like a web service API for transforming images
Use vector images, like SVG. These are built on simple primitives and can scale without losing data or change in the file size
You can resize images online or on your system using tools. Learn how to serve responsive images.
An offscreen image is an image located outside of the visible browser viewport.
The audit fails if the page has offscreen images. Lighthouse lists all offscreen or hidden images in your page, along with the potential savings.
Solution
Load offscreen images only when the user focuses on that part of the viewport. To achieve this, lazy-load these images after loading all critical resources.
There are many libraries available online that will load images depending on the visible viewport. Feel free to use them as per the use case.
Minify CSS and JavaScript
Lighthouse identifies all the CSS and JS files that are not minified. It will list all of them along with potential savings.
Minifiers can do it for you. Webpack users can use mini-css-extract-plugin and terser-webpack-plugin for minifying CSS and JS, respectively.
Serve Images in Next-gen Formats
Following are the next-gen image formats:
Webp
JPEG 2000
JPEG XR
The image formats we use regularly (i.e., JPEG and PNG) have inferior compression and quality characteristics compared to next-gen formats. Encoding images in these formats can load your website faster and consume less cellular data.
Lighthouse converts each image of the older format to Webp format and reports those which ones have potential savings of more than 8 KB.
Solution
Convert all, or at least the images Lighthouse recommends, into the above formats. Use your converted images with the fallback technique below to support all browsers.
This technique of compressing the original textual information uses compression algorithms to find repeated sequences and replace them with shorter representations. It's done to further minimize the total network bytes.
Lighthouse lists all the text-based resources that are not compressed.
It computes the potential savings by identifying text-based resources that do not include a content-encoding header set to br, gzip or deflate and compresses each of them with gzip.
If the potential compression savings is more than 10% of the original size, then the file fails the audit.
Solution
Webpack users can use compression-webpack-plugin for text compression.
The best part about this plugin is that it supports Google's Brotli compression algorithm which is superior to gzip. Alternatively, you can also use brotli-webpack-plugin. All you need to do is configure your server to return Content-Encoding as br.
Brotli compresses faster than gzip and produces smaller files (up to 20% smaller). As of June 2020, Brotli is supported by all major browsers except Safari on iOS and desktop and Internet Explorer.
Don’t worry. You can still use gzip as a fallback.
Preconnect to Required Origins
This section lists all the key fetch requests that are not yet prioritized using <link rel="”preconnect”">.
Establishing connections often involves significant time, especially when it comes to secure connections. It encounters DNS lookups, redirects, and several round trips to the final server handling the user’s request.
Solution
Establish an early connection to required origins. Doing so will improve the user experience without affecting bandwidth usage.
To achieve this connection, use preconnect or dns-prefetch. This informs the browser that the app wants to establish a connection to the third-party origin as soon as possible.
Use preconnect for most critical connections. For non-critical connections, use dns-prefetch. Check out the browser support for preconnect. You can use dns-prefetch as the fallback.
Point all your flagged resources to their current location. It’ll help you optimize your pages' Critical Rendering Path.
Use Video Formats for Animated Content
This section lists all the animated GIFs on your page, along with the potential savings.
Large GIFs are inefficient when delivering animated content. You can save a significant amount of bandwidth by using videos over GIFs.
Solution
Consider using MPEG4 or WebM videos instead of GIFs. Many tools can convert a GIF into a video, such as FFmpeg.
Use the code below to replicate a GIF's behavior using MPEG4 and WebM. It’ll be played silent and automatically in an endless loop, just like a GIF. The code ensures that the unsupported format has a fallback.
Note: Do not use video formats for a small batch of GIF animations. It’s not worth doing it. It comes in handy when your website makes heavy use of animated content.
Final Thoughts
I found a great result in my app's performance after trying out the techniques above.
While they may not all fit your app, try it and see what works and what doesn't. I have compiled a list of some resources that will help you enhance performance. Hopefully, they help.
Do share your starting and final audit scores with me.
Building High-performance Apps: A Checklist To Get It Right
An app is only as good as the problem it solves. But your app's performance can be extremely critical to its success as well. A slow-loading web app can make users quit and try out an alternative in no time. Testing an app’s performance should thus be an integral part of your development process and not an afterthought.
In this article, we will talk about how you can proactively monitor and boost your app’s performance as well as fix common issues that are slowing down the performance of your app.
I’ll use the following tools for this blog.
Lighthouse - A performance audit tool, developed by Google
Webpack - A JavaScript bundler
You can find similar tools online, both free and paid. So let’s give our Vuea new Angularperspective to make our apps Reactfaster.
Performance Metrics
First, we need to understand which metrics play an important role in determining an app’s performance. Lighthouse helps us calculate a score based on a weighted average of the below metrics:
First Contentful Paint (FCP) - 15%
Speed Index (SI) - 15%
Largest Contentful Paint (LCP) - 25%
Time to Interactive (TTI) - 15%
Total Blocking Time (TBT) - 25%
Cumulative Layout Shift (CLS) - 5%
By taking the above stats into account, Lighthouse gauges your app's performance as such:
0 to 49 (slow): Red
50 to 89 (moderate): Orange
90 to 100 (fast): Green
I would recommend going through Lighthouse performance scoring to learn more. Once you understand Lighthouse, you can audit websites of your choosing.
I gathered audit scores for a few websites, including Walmart, Zomato, Reddit, and British Airways. Almost all of them had a performance score below 30. A few even secured a single-digit.
To attract more customers, businesses fill their apps with many attractive features. But they ignore the most important thing: performance, which degrades with the addition of each such feature.
As I said earlier, it’s all about the user experience. You can read more about why performance matters and how it impacts the overall experience.
Now, with that being said, I want to challenge you to conduct a performance test on your favorite app. Let me know if it receives a good score. If not, then don’t feel bad.
Below your scores are the possible opportunities suggested by Lighthouse. Fixing these affects the performance metrics above and eventually boosts your app's performance. So let’s check them out one-by-one.
Here are all the possible opportunities listed by Lighthouse:
Eliminate render-blocking resources
Properly size images
Defer offscreen images
Minify CSS & JavaScript
Serve images in the next-gen formats
Enable text compression
Preconnect to required origins
Avoid multiple page redirects
Use video formats for animated content
A few other opportunities won't be covered in this blog, but they are just an extension of the above points. Feel free to read them under the further reading section.
This section lists down all the render-blocking resources. The main goal is to reduce their impact by:
removing unnecessary resources,
deferring non-critical resources, and
in-lining critical resources.
To do that, we need to understand what a render-blocking resource is.
Render-blocking resource and how to identify
As the name suggests, it’s a resource that prevents a browser from rendering processed content. Lighthouse identifies the following as render-blocking resources:
A <script> </script>tag in <head> </head>that doesn’t have a defer or async attribute
A <link rel="”stylesheet”"> tag that doesn’t have a media attribute to match a user's device or a disabled attribute to hint browser to not download if unnecessary
A <link rel="”import”"> that doesn’t have an async attribute
To reduce the impact, you need to identify what’s critical and what’s not. You can read how to identify critical resources using the Chrome dev tool.
Classify Resources
Classify resources as critical and non-critical based on the following color code:
Green (critical): Needed for the first paint.
Red (non-critical): Not needed for the first paint but will be needed later.
Solution
Now, to eliminate render-blocking resources:
Extract the critical part into an inline resource and add the correct attributes to the non-critical resources. These attributes will indicate to the browser what to download asynchronously. This can be done manually or by using a JS bundler.
Webpack users can use the libraries below to do it in a few easy steps:
For extracting critical CSS, you can use html-critical-webpack-plugin or critters-webpack-plugin. It’ll generate an inline <style></style> tag in the <head></head> with critical CSS stripped out of the main CSS chunk and preloading the main file
For extracting CSS depending on media queries, use media-query-splitting-plugin or media-query-plugin
The first paint doesn't need to be dependent on the JavaScript files. Use lazy loading and code splitting techniques to achieve lazy loadingresources (downloading only when requested by the browser). The magic comments in lazy loadingmake it easy
And finally, for the mainchunk, vendor chunk, or any other external scripts (included in index.html), you can defer them using script-ext-html-webpack-plugin
There are many more libraries for inlining CSS and deferring external scripts. Feel free to use as per the use case.
Use Properly Sized Images
This section lists all the images used in a page that aren’t properly sized, along with the stats on potential savings for each image.
How Lighthouse Calculate Oversized Images?
Lighthouse calculates potential savings by comparing the rendered size of each image on the page with its actual size. The rendered image varies based on the device pixel ratio. If the size difference is at least 25 KB, the image will fail the audit.
Solution
DO NOT serve images that are larger than their rendered versions! The wasted size just hampers the load time.
Alternatively,
Use responsive images. With this technique, create multiple versions of the images to be used in the application and serve them depending on the media queries, viewport dimensions, etc
Use image CDNs to optimize images. These are like a web service API for transforming images
Use vector images, like SVG. These are built on simple primitives and can scale without losing data or change in the file size
You can resize images online or on your system using tools. Learn how to serve responsive images.
An offscreen image is an image located outside of the visible browser viewport.
The audit fails if the page has offscreen images. Lighthouse lists all offscreen or hidden images in your page, along with the potential savings.
Solution
Load offscreen images only when the user focuses on that part of the viewport. To achieve this, lazy-load these images after loading all critical resources.
There are many libraries available online that will load images depending on the visible viewport. Feel free to use them as per the use case.
Minify CSS and JavaScript
Lighthouse identifies all the CSS and JS files that are not minified. It will list all of them along with potential savings.
Minifiers can do it for you. Webpack users can use mini-css-extract-plugin and terser-webpack-plugin for minifying CSS and JS, respectively.
Serve Images in Next-gen Formats
Following are the next-gen image formats:
Webp
JPEG 2000
JPEG XR
The image formats we use regularly (i.e., JPEG and PNG) have inferior compression and quality characteristics compared to next-gen formats. Encoding images in these formats can load your website faster and consume less cellular data.
Lighthouse converts each image of the older format to Webp format and reports those which ones have potential savings of more than 8 KB.
Solution
Convert all, or at least the images Lighthouse recommends, into the above formats. Use your converted images with the fallback technique below to support all browsers.
This technique of compressing the original textual information uses compression algorithms to find repeated sequences and replace them with shorter representations. It's done to further minimize the total network bytes.
Lighthouse lists all the text-based resources that are not compressed.
It computes the potential savings by identifying text-based resources that do not include a content-encoding header set to br, gzip or deflate and compresses each of them with gzip.
If the potential compression savings is more than 10% of the original size, then the file fails the audit.
Solution
Webpack users can use compression-webpack-plugin for text compression.
The best part about this plugin is that it supports Google's Brotli compression algorithm which is superior to gzip. Alternatively, you can also use brotli-webpack-plugin. All you need to do is configure your server to return Content-Encoding as br.
Brotli compresses faster than gzip and produces smaller files (up to 20% smaller). As of June 2020, Brotli is supported by all major browsers except Safari on iOS and desktop and Internet Explorer.
Don’t worry. You can still use gzip as a fallback.
Preconnect to Required Origins
This section lists all the key fetch requests that are not yet prioritized using <link rel="”preconnect”">.
Establishing connections often involves significant time, especially when it comes to secure connections. It encounters DNS lookups, redirects, and several round trips to the final server handling the user’s request.
Solution
Establish an early connection to required origins. Doing so will improve the user experience without affecting bandwidth usage.
To achieve this connection, use preconnect or dns-prefetch. This informs the browser that the app wants to establish a connection to the third-party origin as soon as possible.
Use preconnect for most critical connections. For non-critical connections, use dns-prefetch. Check out the browser support for preconnect. You can use dns-prefetch as the fallback.
Point all your flagged resources to their current location. It’ll help you optimize your pages' Critical Rendering Path.
Use Video Formats for Animated Content
This section lists all the animated GIFs on your page, along with the potential savings.
Large GIFs are inefficient when delivering animated content. You can save a significant amount of bandwidth by using videos over GIFs.
Solution
Consider using MPEG4 or WebM videos instead of GIFs. Many tools can convert a GIF into a video, such as FFmpeg.
Use the code below to replicate a GIF's behavior using MPEG4 and WebM. It’ll be played silent and automatically in an endless loop, just like a GIF. The code ensures that the unsupported format has a fallback.
Note: Do not use video formats for a small batch of GIF animations. It’s not worth doing it. It comes in handy when your website makes heavy use of animated content.
Final Thoughts
I found a great result in my app's performance after trying out the techniques above.
While they may not all fit your app, try it and see what works and what doesn't. I have compiled a list of some resources that will help you enhance performance. Hopefully, they help.
Do share your starting and final audit scores with me.
Velotio Technologies is an outsourced software product development partner for top technology startups and enterprises. We partner with companies to design, develop, and scale their products. Our work has been featured on TechCrunch, Product Hunt and more.
We have partnered with our customers to built 90+ transformational products in areas of edge computing, customer data platforms, exascale storage, cloud-native platforms, chatbots, clinical trials, healthcare and investment banking.
Since our founding in 2016, our team has completed more than 90 projects with 220+ employees across the following areas:
Building web/mobile applications
Architecting Cloud infrastructure and Data analytics platforms