How to improve performance of Next.js website?
Tags: nextjs, react, tutorial, javascript
Published: 2022-06-26 12:38:30




![Website speed](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9s4tginye0omepqzlfgk.gif)

In this post, I'll teach you how to boost the performance of your Next.js website. I'm going to utilize the Lighthouse Chrome add-on to measure performance (now by default it is available). There may also be some simple tips included. Depending upon the type of website, always make sure your score is more than 80. I utilized some methods listed below to improve the performance of my website.

> **Repo**(This is my personal portfolio website) :- https://github.com/Sumukha210/new-portfolio-website

---
### 1) Always use Lighthouse in private mode.

![private mode Image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/slyd9bknk26zrk7h22up.png)




It is a basic strategy, but it is incredibly successful. Because when you run your lighthouse, the performance may suffer owing to external interference such as chrome extensions, scripts, and so on. Also, be certain that you are running a production build. To run the production build, use `yarn run build`.


---
### 2) Lazy loading.

![Smoke effect](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l4o9dpxpgq8x5d8lz9nv.gif)
I utilized a smoke effect on my website by copying and pasting the codepen demo https://codepen.io/Irehan/pen/YgyozL. It works great on the desktop, however it does not operate properly on touch devices. Also, because it uses WebGL, there is a lot of code; to avoid this, I lazily loaded the code. Similar to this
```tsx
const lazyLoadSmokeEffect = async (canvas: any) => {
    const { smokeSimulation } = await import("./SmokeEffect");
    smokeSimulation(canvas); // This functions contains the code
  };

  useIsomorphicLayoutEffect(() => {
    if (canvasRef?.current && window.innerWidth >= 1200) {
      setTimeout(() => {
        lazyLoadSmokeEffect(canvasRef.current);
      }, 2000);
    }
  }, []);
```
---
### 3) Intersection observer to lazily load the Component.

![Lazy load image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lgs190is4sd6vz6gq0zc.gif)
Because my site has a contact form, I used reCAPTCHA to avoid spam (check my article on how to implement Google reCAPTCHA here: https://dev.to/sumukhakb210/integrating-recaptcha-with-nextjs-4ig6), but when I checked the lighthouse tab, it generated scripts that weighed about 143kb. As a result, I employed Next.js Dynamic imports. I also used the useOnScreen custom react hook, which loads the component lazily when the user scrolls to a specific point.

Here, I implemented the Next.js Dynamic imports for **contact section**.

```tsx
import React, { useRef } from "react";
import About from "@/modules/about/About";
import Hero from "@/modules/hero/Hero";
import Layout from "@/modules/Layout";
import Skills from "@/modules/skills/Skills";
import dynamic from "next/dynamic";
import { useOnScreen } from "@/utils/useOnScreen";
import SEO from "@/utils/SEO";
import Project from "@/modules/projects/Project";

const DynamicContactUsComponent = dynamic(
  () => import("@/modules/contact/Contact"),
  {
    loading: () => (
      <p className="loadingText subtitle-4">
        Contact us Loading, please wait...
      </p>
    ),
  }
);

const MainPage = () => {
  const bodyHeight = 800;
  const ContactRef = useRef(null);
  const isContactIntersecting = useOnScreen(ContactRef, `${bodyHeight / 2}px`);

  return (
    <Layout>
      <SEO />
      <Hero />
      <About />
      <Skills />
      <Project />

      <div ref={ContactRef} id="contactSection">
        {isContactIntersecting && <DynamicContactUsComponent />}
      </div>
    </Layout>
  );
};

export default MainPage;
```

**useOnScreen Custom Hook,**
```tsx
import { MutableRefObject, useState } from "react";
import useIsomorphicLayoutEffect from "./useIsomorphicEffect";

export const useOnScreen = (
  ref: MutableRefObject<null>,
  rootMargin: string = "0px"
): boolean => {
  const [isIntersecting, setIntersecting] = useState<boolean>(false);
  useIsomorphicLayoutEffect(() => {
    const observer = new IntersectionObserver(entries => {
      console.log("entries", entries);
      entries.forEach(
        entry => {
          if (entry.isIntersecting) {
            setIntersecting(true);
          }
        },
        { rootMargin }
      );
    });

    if (ref.current) {
      observer.observe(ref.current);
    }
    return () => {
      ref.current && observer.unobserve(ref.current);
    };
  }, []);

  return isIntersecting;
};
```
> Also, don't overdo it because using dynamic for more components increases the amount of requests browser sent to the server.

---
### 4) Using the Babel plugin for styled components to reduce delays while applying styles.

In `.babelrc` file,
```tsx
{
    "presets": [
        "next/babel"
    ],
    "plugins": [
        [
            "styled-components"
        ]
    ]
}
```

In `_document.tsx`
```tsx
import Document, { DocumentContext, DocumentInitialProps } from "next/document";
import { ServerStyleSheet } from "styled-components";

export default class MyDocument extends Document {
  static async getInitialProps(
    ctx: DocumentContext
  ): Promise<DocumentInitialProps> {
    const sheet = new ServerStyleSheet();
    const originalRenderPage = ctx.renderPage;

    try {
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: App => props => sheet.collectStyles(<App {...props} />),
        });

      const initialProps = await Document.getInitialProps(ctx);
      return {
        ...initialProps,
        styles: [
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>,
        ],
      };
    } finally {
      sheet.seal();
    }
  }
}
```

### These are some quick tips to help you enhance your performance:-

- To display images, use the **next/image** component.
- Using the font optimization technique of Next.js
```tsx
// pages/_document.js

import Document, { Html, Head, Main, NextScript } from 'next/document'

class MyDocument extends Document {
  render() {
    return (
      <Html>
        <Head>
          <link
            href="https://fonts.googleapis.com/css2?family=Inter&display=optional"
            rel="stylesheet"
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

export default MyDocument
```
---
Thank you for reading my article. If you have any techniques for improving performance, please feel free to share them in the comments. ✌🖐👍