Web Performance: Optimizing First Input Delay
Tags: javascript, nextjs, performance
Published: 2022-03-18 07:00:00



## Background

At Acko, i help build scalable frontend solutions for the platform team. Here, we have multiple insurance for products ranging from car, bike & health to other mini-insurance products  such as electronics , domestic flight travels , Hotel stays etc.

Our landing pages for all products including [car](https://www.acko.com/car-insurance/), [bike](https://www.acko.com/two-wheeler-insurance/), and [health](https://www.acko.com/arogya-sanjeevani-health-insurance/) act as an entry point to one of the product’s buy journeys. These landing pages include all the necessary content to help users better understand the product and its offerings.

Lately, we started to observe multiple users not engaging well with our product pages. In the reports shared by the analytics team, we found that the product pages had a high bounce rate, and as a result, the visit to lead conversation rate was taking a hit. To put it in simpler terms, many users visiting the page were not going through the product’s buy journey to purchase insurance policies.

As you can see, this was directly having an impact on the revenue and had to be picked on high priority. So, I took the initiative and started exploring all the possible reasons for the user's lousy engagement with our product pages, and after a lot of analysis, found it to be the First Input Delay (FID) metric.

---

## What is First Input Delay?

First Input Delay is the delay between the user’s first interaction with the web page (from discrete actions like clicks, taps, or presses) and the time the browser takes to respond to the user’s action by processing it. Continuous types of user interaction, like zooming or scrolling the page, can’t be accurately measured using this metric.

To put it simply, **FID is the measurement of the time it takes for the browser to respond to the user’s first interaction with the web page**.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zxfuqfeci0onoftr72jb.png)

---

## What is a good FID score?

Web sites should strive to have a First Input Delay of 100 milliseconds or less for a good user experience. For most users, a good threshold to measure is the 75th percentile of page loads, segmented across mobile and desktop devices.

## How to measure FID

The following tools can show you your FID gathered in the field:

- [Chrome User Experience Report](https://developer.chrome.com/docs/crux/)
- [PageSpeed Insights](https://pagespeed.web.dev/?utm_source=psi&utm_medium=redirect)
- [Search Console](https://support.google.com/webmasters/answer/9205520) (Core Web Vitals report)

You can also measure FID using Javascript

- Using the web-vitals JavaScript library.
- Manually adding a PerformanceObserver to track input.

```
import {getFID} from 'web-vitals'; 
getFID(console.log);
```

---

## First Input Delay in detail

FID is a real-user web performance metric and will be the user’s first impression of the site’s responsiveness. Hence, has a direct impact on the actual user experience of visiting the page. From the SEO standpoint, the First input delay will influence the website’s ranking.

As users, we have definitely experienced - loading certain web pages, trying to interact with them after seeing a large part of the content, and then getting frustrated when nothing happens.

In general, this delay happens because - the browser’s main thread is busy parsing, waiting for page resources to be downloaded, and then executing these large JS files rather than responding to users' input events.

![First-Input-Delay shown on page load](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qp88kgwoepv44mtya4h6.png)

These delays typically occur between First Contentful Paint (FCP) and Time to Interactive (TTI) because the page has rendered some of its content but isn’t yet reliably interactive for users to start using the site.

So, as you may have guessed, I had to figure out ways to reduce Javascript execution time and Minimize JS bundle size on Acko’s product pages to tackle bad FID scores. And, now that you have a good understanding of the First Input Delay, let’s jump on to the solutions.

---

**1. Lazy loading non-critical resources**

When you load a web page for the first time, the browser will begin parsing the HTML and requests for the linked resources as and when the browser encounters the links during parsing. And it is important we do not block the parsing or building of the DOM for resources that are non-critical. These resources can be scripts, components, modals, images, or some chunks in your application.

Here, all the product pages at Acko are powered by Nextjs and are server-side rendered. Nextjs provides an advanced feature called [Dynamic Import](https://nextjs.org/docs/advanced-features/dynamic-import) with which you could load the components dynamically either on the client or server. With this, I lazy-loaded several components which were not critical on the [product page](https://www.acko.com/car-insurance/).

e.g. the ‘Get Quote’ floater + modal is now dynamically loaded on the client-side on scroll, Input product widget is also dynamically loaded without blocking the rest of the content from rendering. Most frameworks do provide a way to lazy load resources.

**Image Lazy Loading** is also a very important aspect that you need to consider as images are render-blocking resources. It is implemented already in Nextjs Image Component. You can also look at multiple libraries available online.

**Defer non-critical scripts**. Scripts with defer never block the parsing of the page and always execute when the DOM is ready. I deferred gtag scripts as they were very heavy in size and not critical on the first-page load. The global site tag (gtag. js) is a JavaScript library that allows you to send event data to Google Analytics.

You can also consider dynamically loading scripts on your webpage. Dynamically push `<script>` tags in your website’s head for certain use-cases. For e.g. loading the Paypal script only on the payment page to show payment methods rather than loading and executing this script on every other page.

{% embed https://www.youtube.com/watch?v=X9eRLElSW1c&ab_channel=estellevw %}

**2. Minify and compress payloads**

All our product pages are powered by [Contentful](https://www.contentful.com/) CMS. Contentful CMS is a tool that lets you create, manage and distribute content. Developers are provided with APIs to consume the content. We have configured brotli compression of the content, and caching of the pages. You can configure this in your cms/framework by referring to respective documentation, as all major frameworks have the capability to do this.

**3. Remove unused code and optimize/remove heavy components**

There were a lot of unused dependencies and JS/CSS files in our codebase which we had to carefully flush out without breaking our Application. You. can find the unused dependencies in your project with [depcheck](https://www.npmjs.com/package/depcheck).

To minimize the bundle size more. Configure [Bundle Analyzer](https://www.npmjs.com/package/@next/bundle-analyzer) in your React/Next JS app to get a complete picture of your bundles that could be taking up most spaces on the client and the server. You can use dynamic imports, to split your code into manageable chunks.

**4. Reduce Next JS bundle size with Preact**

This is specific to Nextjs. Within the Next JS webpack config file, you can add in preact configuration to essentially replace react with preact at client production build. React with React-dom takes up 42kb at runtime, which can be avoided by utilizing Preact (which is 3Kb in size) without any extra overhead.

```
module.exports = {
  webpack: (config, { dev, isServer }) => {
    if (!dev && !isServer) {
      Object.assign(config.resolve.alias, {
        react: 'preact/compat',
        'react-dom/test-utils': 'preact/test-utils',
        'react-dom': 'preact/compat',
      })
    }
    return config
  },
}
```


**5. Optimizing your Images**

Compress your images. Convert to [WebP image formats](https://developers.google.com/speed/webp). WebP images have a 25–30% smaller file size than a JPEG/png image. In my case, Contentful provides a way to display WebP images with a JPEG fallback. Most CMS should definitely provide ways to configure WebP images.

```

import Image from 'next/image'

const contentfulImageLoader = ({ src, width, quality }) => {
  return `https://cdn.contentful/${src}fm=webp&w=${width}&q=${quality || 75}`
}

function CardImage() {
  return (
    <Image
      loader={contentfulImageLoader}
      src="image-src"
      alt="image-alt-text"
      width={500}
      height={500}
    />
  )
}

```
---

**Overall FID Improvements we have observed on Acko's product pages**

Post-release, FID scores for most pages that were earlier in the range of 150–300ms are now under 100ms, and from the reports shared bounce rate has significantly improved leading to a better conversion rate.

I really enjoyed working on this and have learned quite a lot about web performance. There is still room for improvement, and we are constantly working towards providing a better user experience.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pkixzlbdfbf9ymkwawp4.png)

---

Note: Improvement in FID score cannot be seen immediately after your release or even the next week. Field Data is calculated on a cumulative basis and I got to observe gradual improvement in the score over a period of 1-2 months . (PageSpeed Insights: https://pagespeed.web.dev/).

---

**Resources**

https://web.dev/mainthread-work-breakdown/

https://joyofcode.xyz/next-bundle-size

https://nitropack.io/blog/post/fid-optimize

https://uploadcare.com/blog/next-js-image-optimization/

https://web.dev/reduce-network-payloads-using-text-compression/

https://web.dev/script-component/

That’s it, Folks.