Boost NextJS TTI & FID performance without compromise and pain
Tags: nextjs, performance, react, javascript
Published: 2022-06-08 07:22:52

## Why should you read this blog?

- As the title said: “Boost NextJS TTI & FID performance without compromise and pain”
- Islands Architectures for Nextjs

## Result

### Before

![Before](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7sjm61mo4u0pf7s2ipsm.png)
 

[https://next-lazy-hydrate-origin.vercel.app/](https://next-lazy-hydrate-origin.vercel.app/)

[Live check PageSpeed](https://pagespeed.web.dev/report?url=https%3A%2F%2Fnext-lazy-hydrate-origin.vercel.app%2F&form_factor=mobile)

### After

![After](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hf2ut4mivz3dyfyln3u3.png)
 

[https://next-lazy-hydrate-optimized.vercel.app/](https://next-lazy-hydrate-optimized.vercel.app/)

[Live check PageSpeed](https://pagespeed.web.dev/report?url=https%3A%2F%2Fnext-lazy-hydrate-optimized.vercel.app%2F&form_factor=mobile)

## Hydrating is PURE OVERHEAD

[Hydration is Pure Overhead](https://www.builder.io/blog/hydration-is-pure-overhead)
![SSR process](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jzsezzk3rl9b6rxo4hne.png)
 

As described in the post above, `Hydration` progress is PURE OVERHEAD since you need to load the code and render the component twice.

Imagine you have a very long landing page built by Nextjs and most of it is a static component, then when you hit the Enter in the URL:

1. HTML contains all your landing page content sent to the browser (Which is the result of SSR)
2. JavaScript is downloaded to the browser, get parsed, and executed (Most of it contains text content only which is nearly the same as your HTML)
3. Which Javascript downloaded, now it attaches events to the DOM. Now your website is fully usable

The second moves make most of SSR page has TTI (Time To Interactive) and FID (First Input Delay) so high

## Progressive Hydration

Let’s take a step to optimize our long-landing-page. Because on our landing page, most of the component is static (Only text and image, nothing much called “interactive”) so it’s a waste of time to hydrate those components. What if we disable hydrate for some components or only hydrate components when it’s in the `Viewport`

![Progressive Hydration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/065v77zaxjmam3pn5hnx.png)
 

This can easily archive using `react-hydration-on-demand` 

```jsx
import withHydrationOnDemand from "react-hydration-on-demand";
import Card from "../Card";

// Hydrate when the component enters the viewport
const CardWithHydrationOnDemand = withHydrationOnDemand({ on: ["visible"] })(
    Card
);

export default class App extends React.Component {
    render() {
        return (
            <CardWithHydrationOnDemand
                title="my card"
                wrapperProps={{
                    className: "customClassName",
                    style: { display: "contents" },
                }}
            />
        );
    }
}
```

Now you can optimize the 3rd bullet - Reduce the time JavaScript executed to hydrate our landing page. Good job!

## Lazy load component code and hydrate when needed

We can save some executed time using `react-hydration-on-demand` but we still have lots of redundancy code here.

> JavaScript of those components is still downloaded and parsed, it just doesn’t get executed.
Do we have any way to fully render the HTML of the website but only load the component’s JS only when needed?
> 

There is an answer for that: [https://www.patterns.dev/posts/islands-architecture/](https://www.patterns.dev/posts/islands-architecture/)

![Islands Architecture](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5i6ivpizq0offul6wl6h.png)
 

The idea is quite simple:

- Fully render HTML in SSR
- Load a really minimum of JavaScript to listen to the events
- If an event is fired, load the JS related to it and executed

This solution comes with a huge performance boost by scarifying a little time between every user’s interactive. But I do think it worse doing so 🌟

![Disable Javascript reduces the TTI more than 7 times. What if we can remove half of it?
](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9e4dpbsjux20hrnokflt.png)
 

Disable Javascript reduces the TTI more than 7 times. What if we can remove half of it?

This is nice! The solution is simple but quite hard to do. Why?

- Because Reactjs only supports hydrating a full application ([It will be solved when v18 is fully implemented](https://github.com/reactwg/react-18/discussions/37)). The `react-hydration-on-demand` actually do some trick to skip the hydrating process
- In Nextjs, if the component is defined as `dynamic` and it renders in SSR, its JS also gets sent to the browser right away so nothing called `lazy` here

Read more

[Why Progressive Hydration is Harder than You Think](https://www.builder.io/blog/why-progressive-hydration-is-harder-than-you-think)

So I make a package that can

- Skip the component hydrating process. Heavily based on `react-hydration-on-demand`
- Remove the JS from the bundle and make you control when the JS is loaded

How can I do this trick? [Check it out](https://github.com/thanhlmm/next-lazy-hydrate/blob/main/src/index.tsx)

Here is the result

{% embed https://vimeo.com/718200286 %}

### How to use it

**Install**

```bash
npm install next-lazy-hydrate
yarn add next-lazy-hydrate
```

**Usage**

```jsx
import lazyHydrate from 'next-lazy-hydrate';

// Static component
const WhyUs = lazyHydrate(() => import('../components/whyus'));

// Lazy hydrate when users hover the component
const Footer = lazyHydrate(
  () => import('../components/footer', { on: ['hover'] })
);

const HomePage = () => {
  return (
    <div>
      <AboveTheFoldComponent />
      {/* ----The Fold---- */}
      <WhyUs />
      <Footer />
    </div>
  );
};
```

**Document**

[https://github.com/thanhlmm/next-lazy-hydrate](https://github.com/thanhlmm/next-lazy-hydrate)

The API is quite simple, I’d love to see how this package can help you **Boost NextJS TTI & FID performance without compromise and pain**

Original post: https://thanhle.blog/en/blog/boost-nextjs-tti-fid-performance-without-compromise-and-pain