How to Optimize Performance in your Next.js App πŸ§™β€β™‚οΈπŸͺ„

How to Optimize Performance in your Next.js App πŸ§™β€β™‚οΈπŸͺ„

September 25, 2024 (1mo ago)

web-developmentreactjsnextjs

Introduction

"First impression is the last impression" - It's also true for User experience. If your application does not perform well at first glance, it can create a negative experience.

Recently, I was working on one of my side projects and after some time, I noticed -

"Why is the app moving so slowly?"

GIF

Then I thought of optimizing my Next.js application to improve its overall experience. I explored and implemented various techniques to achieve this.

In this article, I'll share how to optimize the performance of our next.js application and some tips regarding that!

So, Without delaying any further, let's START!

Built-in Optimizations

Next.js provides us with some built-in components that help to optimize the overall performance of our application.

Image Optimization

Responsive image filling the width and height of its parent container

One of the most common reasons of performance issues is unoptimized images. It does play a big role in typical website’s page weight and can have a sizable impact on your website's LCP performance.

To solve this problem, Next.js provides us the <Image /> component that optimizes the images by default. It extends the HTML <img> element with some optimization features:

  1. Size Optimization: Next.js automatically determines the height and width for local images and makes sure that images are served in the appropriate sizes. This reduces both bandwidth usage and load time.
  2. Visual Stability: Using width and height attributes Next.js determines the correct aspect ratio of the image and avoids layout shift from the image loading in. This is why the height and width of an image is required by default.
  3. Faster page loading: Next.js uses a native browser lazy loading and loads the images only when it's required that is when it comes in the viewport. It also uses other techniques like caching, and preloading to reduce the load time.

An example of the use of the <Image /> component:

import Image from "next/image";
import profilePic from "./me.png";

export default function Page() {
  return (
    <Image
      src={profilePic}
      alt="Picture of the author"
      // width={500} automatically provided
      // height={500} automatically provided
      // blurDataURL="data:..." automatically provided
      // placeholder="blur" // Optional blur-up while loading
    />
  );
}

The next/image component and Next.js Image Optimization API can be configured in the next.config.js file. If you want to know more about next/image, check this:

Another out-of-the-box optimization feature that Next.js provides is the <Link /> component. Unlike the traditional <a></a> tags next/link component prefetches a page in the background and improves the overall navigation performance.

When a <Link /> component enters the user's viewport, Next.js prefetches and loads the linked route (denoted by the href) and its data in the background to improve the performance of client-side navigations.

Note: Prefetching is only enabled in production.

Additionally, we can pass props to them next/link to give us better control over the navigation behavior.

An example use of Link Component:

import Link from "next/link";

export default function Page() {
  return (
    <Link href="/dashboard" prefetch={false}>
      Dashboard
    </Link>
  );
}

Font Optimization

Next.js provides next/font component that optimizes font. This component prevents external network requests for fonts and improves performance and enforces privacy.

next/font also provides built-in automatic self-hosting for any font file. With this, we can optimally load web fonts with zero layout shift. It can smoothly incorporate any Google font into our application and no requests are sent to Google by the browser.

An Example of the Font Component:

import { Inter } from 'next/font/google'

// If loading a variable font, you don't need to specify the font weight
const inter = Inter({
  subsets: ['latin'],
  display: 'swap',
})

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" className={inter.className}>
      <body>{children}</body>
    </html>
  )
}

πŸŽ₯ Watch: Learn more about using next/font:

Script Optimization

Third-party scripts like analytics and social media widgets significantly impact the performance and user experience of our Application.

To solve that, Next.js provides next/script component that optimally loads third-party scripts and optimizes the loading time of the application. Next.js makes sure that the script will only load once, even if a user navigates between multiple pages.

Here's an example Code:

import Script from "next/script";

export default function MyApp({ Component, pageProps }) {
  return (
    <>
      <Component {...pageProps} />
      <Script src="https://example.com/script.js" />
    </>
  );
}

By default, next/script allows us to load third-party scripts on any page or layout. However, we can fine-tune its loading behavior by using the strategy property:

Dynamic Imports

We often load the Client Components and imported libraries in the initial app/page load which significantly increases the Page Load time and impacts user experience.

Next.js Supports dynamic imports, so instead of importing everything in the initial load, we can dynamically import them only when they are required. It reduces the initial bundle size and improves the load time of our application.

Here's an example of this:

import dynamic from "next/dynamic";

// Dynamic Component:
const DynamicComponent = dynamic(
  () => import("../components/DynamicComponent"),
);

export default function DynamicComponentExample() {
  return (
    <div>
      <DynamicComponent />
    </div>
  );
}

Bundle Analyzer

By bundling external packages we can significantly improve the performance of our application. By Default, Next.js automatically bundles packages imported inside Server Components.

Next.js also provides a plugin @next/bundle-analyzer to manage the size of our application bundles. It generates a visual report of the size of each package and their dependencies.

Based on that report, we can identify the packages that are taking up large spaces to then split them and lazy load some parts them to Optimize performance.

Remove unused packages

We often forget to remove unused packages defined in the package.json which creates unnecessary load for our application and impacts its performance.

We can easily identify such unused packages using tools like depcheck . It gives us the list of unused packages which makes it easier for us to identify and remove them.

With that, We can reduce load from our application and get optimal performance.

A terminal window showing the results of running the command . The output lists unused dependencies, unused devDependencies, and missing dependencies for a project.

Remove unused CSS and JS

Just like packages, unused JavaScript and CSS also affects our application's performance. There are various tools to remove them to reduce bundle size and optimize performance.

Tools like PurgeCSS help us to remove unused CSS from our application. Similarly, tools like Terser also helps us to minify your JavaScript files, reducing their size and improving your application’s LCP.

Conclusion

In this article, I've tried to cover some ways to optimize the performance of your Next.js Application.

However, there are many other ways to improve your app performance out there, If you know some more ways, feel free to write them in the comments.

If you found this blog post helpful, please consider sharing it with others who might benefit. You can also follow me for more content on Javascript, React, and other web Development topics.

For Paid collaboration mail me at: arindammajumder2020@gmail.com

Connect with me on Twitter, LinkedIn, YouTube and GitHub.

Thank you for Reading : )

Thank You