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

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

Live check PageSpeed

ter

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

Live check PageSpeed

Hydrating is PURE OVERHEAD

https://www.builder.io/blog/hydration-is-pure-overhead

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

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" },
}}
/>
);
}
}

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/

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?

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

https://user-images.githubusercontent.com/9281080/172079813-a49db8c0-c64d-4589-941d-bf027b22433a.mov

How to use it

Install

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

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>
);
};

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

--

--

--

Just blog about myselft

Love podcasts or audiobooks? Learn on the go with our new app.

Creating React Project from scratch

MASAI Journey

Higher Order Guards (Functions)

Scope, Lexical Scope and Closure in JavaScript

Game of Life

Test Automation with Cypress for Beginners

Where do string methods come from?

My First Try On TypeScript Programming

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Thành Lê

Thành Lê

Just blog about myselft

More from Medium

Next.js Static Site Generation Practical Example

Using React 18’s Suspense to Improve Code Quality of Web Loaders

Using React 18’s Suspense to Improve Code Quality of Web Loaders

Synchronous State in React Using Hooks

Building and Sharing React SVG Icon Components