DEV Community

Cover image for How to improve performance of Next.js website?
Sumukhakb
Sumukhakb

Posted on

How to improve performance of Next.js website?

Website speed

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

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

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

3) Intersection observer to lazily load the Component.

Lazy load image
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.

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

useOnScreen Custom Hook,

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

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,

{
    "presets": [
        "next/babel"
    ],
    "plugins": [
        [
            "styled-components"
        ]
    ]
}
Enter fullscreen mode Exit fullscreen mode

In _document.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();
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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
// 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
Enter fullscreen mode Exit fullscreen mode

Thank you for reading my article. If you have any techniques for improving performance, please feel free to share them in the comments. βœŒπŸ–πŸ‘

Top comments (7)

Collapse
 
jcubic profile image
Jakub T. Jankiewicz

I have a question, are those techniques tested only on website that can be HTML+CSS? Because your website look like it doesn't have any dynamic parts, except those lazy loading, that can probably be coded with few lines of Vanilla JavaScript.

I don't want to be rude, but I'm just wondering what is the point of using NextJS for website like this, except as an exercise or to showcase your skills. Because from users perspective it don't make much sense to me.

Collapse
 
sumukhakb210 profile image
Sumukhakb • Edited

Jakub every website is build with HTML and CSS. Yes, you can certainly write this using simply HTML, CSS, and JavaScript. However, you should use any server (perhaps NodeJS or PHP) to hide secrets (on my website, I use email password like this) and to send emails and retrieve blogs from the dev.to API (which needs data to fetch from server otherwise it throws CORS error). Also, Next.js and Nuxt.js provide an excellent developer experience by allowing you to eliminate duplicating code and construct components. As far as the user is concerned, you may still use PHP if you wish; the user just cares about performance. Also, in the next days, I intend to build a blog system instead of directing users to dev.to; in that case, Next.js will undoubtedly come in handy. Also working plain JavaScript kind of pain in the a**πŸ˜πŸ˜πŸ˜πŸ˜‰

Collapse
 
jcubic profile image
Jakub T. Jankiewicz

You can't actually see your website so I didn't know that you have a blog section, looking at screenshots it looks like trial to do with just HTML+CSS not much JS code need to be done. That's why I though that it doesn't make sense.

Collapse
 
simeg profile image
Simon Egersand 🎈

I'll definitely try this for my NextJS application. Thanks!

Collapse
 
fasterthanlight profile image
Faster Than Light

Thank you for the article! For our website, we have also recently tried to improve performance to score higher on Lighthouse. We also gathered some insights and gaps we had here.

Collapse
 
andrewbaisden profile image
Andrew Baisden

Great useful tips! Next.js is gaining so much traction right now.

Collapse
 
sumukhakb210 profile image
Sumukhakb

Thank you for reading. Yes, because it offers nice features aside from styling approaches.