Development

Next.js Performance Optimization: Practical Guide to Speed Up Your Apps

Learn how to speed up Next.js apps with image optimization, code splitting, static generation, font loading, and reducing bundle size.

By Mohamed DjoudirJune 07, 20256 min read
Share:
Next.js Performance Optimization: Practical Guide to Speed Up Your Apps
#Next.js#Performance#Optimization#React

Next.js 14/15 Upgrade Notes:

  • Minimum Node.js version is now 18.17.
  • The next export command is replaced by output: 'export' in next.config.js.
  • The @next/font package is fully removed; use the built-in next/font.
  • APIs like cookies, headers, and draftMode are now async in Next.js 15 (use await).
  • fetch requests are no longer cached by default (cache: 'force-cache' if you need caching).
  • Some config options have changed (experimental-edgeedge).
  • If you use TypeScript, update @types/react and @types/react-dom.
    See the official upgrade guide for more.

Building fast web apps is essential. Next.js provides many built-in performance features, but developers still need to use them wisely. In this guide, we'll cover practical techniques for speeding up Next.js apps. We'll dive into image optimization, code splitting and lazy loading, static generation, optimized font loading, and reducing JavaScript bundle sizes. Along the way, you'll find code examples and tips you can apply immediately.

Image Optimization

Large or unoptimized images are a common performance bottleneck. Next.js's built-in <Image> component helps automatically optimize images for you. It serves correctly-sized images in modern formats (like WebP/AVIF) and enables lazy loading by default. For example, simply use <Image> in your component:

1import Image from "next/image";
2
3export default function Hero() {
4  return (
5    <div>
6      <Image
7        src="/images/hero.jpg"
8        alt="Hero banner"
9        width={1200}
10        height={600}
11        priority
12      />
13      <p>Welcome to the site!</p>
14    </div>
15  );
16}

Image Component Features

  • Responsive Images: The <Image> component automatically serves images in sizes best suited for the user's device.
  • Lazy Loading: Images are lazy-loaded by default, meaning they only load when they enter the viewport, which speeds up initial page load.
  • Placeholder Blur: You can add a blur-up effect while the image is loading by using the placeholder="blur" prop.
  • Custom Loader: For advanced use cases, you can define a custom loader function to control how images are loaded.

Example: Responsive and Lazy-Loaded Image

Here's an example of an image that is both responsive and lazy-loaded:

1<Image
2  src="/images/hero.jpg"
3  alt="Hero banner"
4  layout="responsive"
5  width={1200}
6  height={600}
7  priority
8/>

Code Splitting and Lazy Loading

Code splitting is a technique to split your code into smaller bundles which can then be loaded on demand. Next.js does this automatically for pages, but you can also manually code-split components.

Dynamic Imports

Use dynamic imports to load components only when they are needed. This can be done using the next/dynamic module. For example:

1import dynamic from "next/dynamic";
2
3const DynamicComponent = dynamic(() => import("../components/HeavyComponent"));
4
5export default function Page() {
6  return <DynamicComponent />;
7}

Route-Based Code Splitting

Next.js automatically splits your code at the page level. This means that each page only loads the JavaScript it needs. However, you can further optimize by using dynamic imports for components that are not immediately necessary.

Static Generation

Static generation is the preferred way to generate pages in Next.js. It pre-renders pages at build time, which means the HTML is generated and served as static files. This is great for performance and SEO.

getStaticProps

Use getStaticProps to fetch data at build time. This function runs at build time in production and allows you to fetch data and pass it to your page component as props.

1export async function getStaticProps() {
2  const res = await fetch("https://api.example.com/data");
3  const data = await res.json();
4
5  return {
6    props: {
7      data,
8    },
9  };
10}

Incremental Static Regeneration (ISR)

Next.js 12 introduced ISR, which allows you to update static content after you’ve built your site. You can use the revalidate property in getStaticProps to specify how often a page re-generates.

1export async function getStaticProps() {
2  const res = await fetch("https://api.example.com/data");
3  const data = await res.json();
4
5  return {
6    props: {
7      data,
8    },
9    revalidate: 10, // Regenerate at most once every 10 seconds
10  };
11}

Optimized Font Loading

Fonts can be a significant part of your bundle size and can impact performance. Next.js 13 introduced the next/font package to optimize font loading.

Using next/font

To use the new font system, import fonts from next/font instead of using traditional CSS @font-face rules. For example:

1import { Roboto } from "next/font/google";
2
3const roboto = Roboto({
4  subsets: ["latin"],
5  weight: "400",
6});
7
8export default function Page() {
9  return (
10    <div className={roboto.className}>
11      <h1>Hello, world!</h1>
12    </div>
13  );
14}

Benefits of next/font

  • Automatic Subsetting: Only the characters used on the page are loaded.
  • Variable Fonts: Support for variable fonts, which can reduce the number of font files needed.
  • Optimized Loading: Fonts are loaded in a way that does not block the rendering of the page.

Reducing JavaScript Bundle Size

A smaller JavaScript bundle means faster load times. Here are some strategies to reduce your bundle size:

  • Analyze your bundle: Use the next/bundle-analyzer to see what's in your JavaScript bundle and identify opportunities to reduce its size.
  • Remove unused dependencies: Regularly audit your dependencies and remove any that are not being used.
  • Use lighter alternatives: For example, use date-fns instead of moment.js for date manipulation, as it's significantly smaller.
  • Optimize lodash imports: Instead of importing the entire lodash library, import only the functions you need.

Example: Using next/bundle-analyzer

To use the bundle analyzer, install it first:

npm install @next/bundle-analyzer

Then, update your next.config.js:

const withBundleAnalyzer = require("@next/bundle-analyzer")({
  enabled: process.env.ANALYZE === "true",
});
module.exports = withBundleAnalyzer({});

Now, you can analyze your bundle by running:

ANALYZE=true next build

Conclusion

Performance optimization is critical in delivering a fast, user-friendly web experience. Next.js offers powerful features to help developers optimize their applications effectively. By leveraging image optimization, code splitting, static generation, optimized font loading, and diligent bundle size management, you can significantly enhance the performance of your Next.js applications. Remember to stay updated with the latest Next.js releases and best practices to keep your applications running smoothly and efficiently.

Found this article helpful?

Share:

Global User Community

Join thousands of developers worldwide who trust Aniq-UI for their projects. Our templates are being used across the globe to create stunning web experiences.

world map