DEV Community

Thanh Minh
Thanh Minh

Posted on

Boost NextJS TTI & FID performance without compromise and pain

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://next-lazy-hydrate-origin.vercel.app/

Live check PageSpeed

After

After

https://next-lazy-hydrate-optimized.vercel.app/

Live check PageSpeed

Hydrating is PURE OVERHEAD

Hydration is Pure Overhead
SSR process

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

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

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" },
                }}
            />
        );
    }
}
Enter fullscreen mode Exit fullscreen mode

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/

Islands Architecture

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?<br>

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). 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

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

Here is the result

How to use it

Install

npm install next-lazy-hydrate
yarn add next-lazy-hydrate
Enter fullscreen mode Exit fullscreen mode

Usage

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>
  );
};
Enter fullscreen mode Exit fullscreen mode

Document

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

Top comments (0)