Optimizing Performance in Next.js

~ Index Introduction Image Optimization with next/image Lazy Loading Content the Right Way Font Optimization with next/font Dynamic Imports for Component Splitting 2 Bonus Tips Conclusion ~ Introduction The thing we all hate is when the Website takes too long to load and especially when you have your own website the last thing you want to hear from your visitors is that your Website is slow AF. Performance is crucial for improved SEO rankings, better user experience, and increased conversions. With Core Web Vitals becoming a key part of Google’s search algorithm, even small optimizations that you can take in Next.js can have a measurable impact on your product’s visibility and success. Next.js has from all the react Frameworks the best Performance architecture, from automatic code splitting and server-side rendering to built-in image optimization and edge-ready middleware. But to truly unlock its potential, you have to dig deeper and fine-tune the details. In this guide, I’ll walk you through real-world techniques for optimizing your Next.js app, including: Image optimization with next/image Lazy loading for heavy components Font loading with next/font Dynamic imports for smarter bundle sizes Whether you're building a SaaS dashboard, a blog, or a content-heavy web app, these tips will help you squeeze out every bit of performance and deliver a faster, smoother experience to your users. Let’s dive in!!! ~ Image Optimization with next/image To not tortue your Website Visitor with endless waiting untill the 6MB Image is finally loaded, Next.js came up with a optimization which drastically reduces the load time, improves Largest Contentful Paint (LCP), and cut down on bandwidth usage, especially for mobile users. In addition, it's responsive (On-demand image resizing) and it prevents layout shift from happening when images are loading. How? Instead of using the conventional tag, we can use the Component from Next.js. You first import the Image Component from Next.js: import Image from 'next/image' and then you can either pass your local Image or a Remote Image in the src parameter: Local Image import Image from 'next/image' import profilePic from '../public/me.png' export default function Page() { return ( ) } Remote Image import Image from 'next/image' export default function Page() { return ( ) } Resources and further information ~ Lazy Loading Content the Right Way When you hear Lazy Loading, just think of a technique where resources (like images, JavaScript, or CSS) are loaded only when they're needed, rather than loading everything at once when the page loads. It's crucial for your website's performance because it reduces initial load time, saves bandwidth, and improves user experience, especially on slower networks or less powerful devices. When? You should use Lazy loading for: Images (see Chapter 1) Heavy components (charts, maps, video players, etc.) Third-party scripts and widgets (ads, comment sections, etc.) Example: We create a Chart Component which will be lazy loaded with dynamic from Next.js into the page.tsx component. /components/Chart.tsx export default function Chart() { return chart component!; } /app/Page.tsx import dynamic from 'next/dynamic'; import { useState } from 'react'; // Lazy load the Chart component const LazyChart = dynamic(() => import('../components/Chart'), { loading: () => Loading chart..., ssr: false, // optional: disables server-side rendering for this component }); export default function Home() { const [showChart, setShowChart] = useState(false); return ( Welcome to the dashboard setShowChart(true)}>Show Chart {showChart && } ); } Real-world tip: Combine lazy loading with viewport detection (react-intersection-observer or useInView), so the Lazy Loading will only start loading when the component is in the sight of the user. Resources and further information ~ Font Optimization with next/font Fonts can make a whole lot of chaos in your design symmetry and performance if they're not handled properly. Custum fonts, especially when loaded from external sources often evoke *FOIT *(Flash of Invisible Text), *FOUT *(Flash of Unstyled Text), or layout shifts, which can all F up your Core Web Vitals. Why? FOIT (Flash of Invisible Text): The browser waits to display text until the font loads which leads to leaving a blank screen. FOUT (Flash of Unstyled Text): The text first renders in a fallback font, then snaps into the custom font which causes visible flickers. Layout Shifts: If the fallback font has different metrics (line height, width), the layout visibly jumps once the custom font loads which then leads to impacting Cumulative Layout Shift (CLS). Next.js suprisingly has a solution for this! Next.js introduced the next/font mod

Apr 22, 2025 - 21:02
 0
Optimizing Performance in Next.js

~ Index

  • Introduction
  • Image Optimization with next/image
  • Lazy Loading Content the Right Way
  • Font Optimization with next/font
  • Dynamic Imports for Component Splitting
  • 2 Bonus Tips
  • Conclusion

~ Introduction

The thing we all hate is when the Website takes too long to load and especially when you have your own website the last thing you want to hear from your visitors is that your Website is slow AF.

Performance is crucial for improved SEO rankings, better user experience, and increased conversions. With Core Web Vitals becoming a key part of Google’s search algorithm, even small optimizations that you can take in Next.js can have a measurable impact on your product’s visibility and success. Next.js has from all the react Frameworks the best Performance architecture, from automatic code splitting and server-side rendering to built-in image optimization and edge-ready middleware. But to truly unlock its potential, you have to dig deeper and fine-tune the details.

In this guide, I’ll walk you through real-world techniques for optimizing your Next.js app, including:

  1. Image optimization with next/image
  2. Lazy loading for heavy components
  3. Font loading with next/font
  4. Dynamic imports for smarter bundle sizes

Whether you're building a SaaS dashboard, a blog, or a content-heavy web app, these tips will help you squeeze out every bit of performance and deliver a faster, smoother experience to your users.

Let’s dive in!!!

~ Image Optimization with next/image

To not tortue your Website Visitor with endless waiting untill the 6MB Image is finally loaded, Next.js came up with a optimization which drastically reduces the load time, improves Largest Contentful Paint (LCP), and cut down on bandwidth usage, especially for mobile users. In addition, it's responsive (On-demand image resizing) and it prevents layout shift from happening when images are loading.

How?

Instead of using the conventional tag, we can use the Component from Next.js. You first import the Image Component from Next.js:

import Image from 'next/image'

and then you can either pass your local Image or a Remote Image in the src parameter:

Local Image

import Image from 'next/image'
import profilePic from '../public/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
    />
  )
}

Remote Image

import Image from 'next/image'

export default function Page() {
  return (
    <Image
      src="https://s3.amazonaws.com/my-bucket/profile.png"
      alt="Picture of the author"
      width={500}
      height={500}
    />
  )
}

Resources and further information

~ Lazy Loading Content the Right Way

When you hear Lazy Loading, just think of a technique where resources (like images, JavaScript, or CSS) are loaded only when they're needed, rather than loading everything at once when the page loads. It's crucial for your website's performance because it reduces initial load time, saves bandwidth, and improves user experience, especially on slower networks or less powerful devices.

When?

You should use Lazy loading for:

  • Images (see Chapter 1)
  • Heavy components (charts, maps, video players, etc.)
  • Third-party scripts and widgets (ads, comment sections, etc.)

Example:

We create a Chart Component which will be lazy loaded with dynamic from Next.js into the page.tsx component.

/components/Chart.tsx

export default function Chart() {
  return <div>chart component!</div>;
}

/app/Page.tsx

import dynamic from 'next/dynamic';
import { useState } from 'react';

// Lazy load the Chart component
const LazyChart = dynamic(() => import('../components/Chart'), {
  loading: () => <p>Loading chart...</p>,
  ssr: false, // optional: disables server-side rendering for this component
});

export default function Home() {
  const [showChart, setShowChart] = useState(false);

  return (
    <div>
      <h1>Welcome to the dashboard</h1>
      <button onClick={() => setShowChart(true)}>Show Chart</button>
      {showChart && <LazyChart />}
    </div>
  );
}

Real-world tip:
Combine lazy loading with viewport detection (react-intersection-observer or useInView), so the Lazy Loading will only start loading when the component is in the sight of the user.

Resources and further information

~ Font Optimization with next/font

Fonts can make a whole lot of chaos in your design symmetry and performance if they're not handled properly. Custum fonts, especially when loaded from external sources often evoke *FOIT *(Flash of Invisible Text), *FOUT *(Flash of Unstyled Text), or layout shifts, which can all F up your Core Web Vitals.

Why?

  • FOIT (Flash of Invisible Text): The browser waits to display text until the font loads which leads to leaving a blank screen.
  • FOUT (Flash of Unstyled Text): The text first renders in a fallback font, then snaps into the custom font which causes visible flickers.
  • Layout Shifts: If the fallback font has different metrics (line height, width), the layout visibly jumps once the custom font loads which then leads to impacting Cumulative Layout Shift (CLS).

Next.js suprisingly has a solution for this! Next.js introduced the next/font module, which replaces old school font loading methods like @import or . Its perfectly for performance improvements and also SSR-friendly. It only loads required subsets and weights, keeping bundles small and works just fine with both, local and Google Fonts.

Example: app/layout.tsx

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

const inter = Inter({
  subsets: ['latin'], // only load what's needed
  display: 'swap',    // optional: allows fallback text to show
});

export const metadata = {
  title: 'Follow me on Twitter: luan_ajeti',
};

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

Resources and further information

~ Dynamic Imports for Component Splitting

We often include components that users don’t need immediately like e.g. charts, modals, dashboards etc. Loading them all upfront can lead to huge JavaScript bundles, longer load times, and slow performance. That's where Bundle Splitting comes into the game. It reduces initial JavaScript payload, improves FCP (First Contentful Paint) and TTI (Time to Interactive) and it also helps the browser to prioritize critical UI first.

How?

Create a Dynamic Component:

export default function ComponentSplitting() {
  return <p>Comment if you got any questions!</p>;
}

Use Dynamic on ComponentSplitting() Component:

import dynamic from 'next/dynamic';

const DynamicComponent = dynamic(() => import('../components/ComponentSplitting'), {
  loading: () => <p>Loading...</p>,
});

export default function Home() {
  return (
    <div>
      <h1>Welcome to Next.js!</h1>
      <DynamicComponent />
    </div>
  );
}

Resources and further information

~ 2 Bonus Tips

Beyond image optimization, lazy loading, font handling, and dynamic imports, there are a few more tweaks you can do that are effortles but have a high impact which you can apply to improve performance in your Next.js app.

1. Enable Compression with next.config.js

If you're self-hosting (e.g., with Node or Docker), enabling Gzip or Brotli compression can significantly reduce the size of your responses.

However, Next.js doesn’t handle compression itself — you need to enable it in your server layer (like Express or Nginx).

If you're using a custom Express server:

server.js

const express = require('express');
const compression = require('compression');

const app = express();
app.use(compression());

If you're deploying on Verce it's being automatically taken care of.

2. Analyze Your Bundle Size

Want to see what's bloating your app? Use next-analyze to visualize and optimize your JavaScript bundles.

Install:

npm install --save-dev @next/bundle-analyzer

Add to next.config.js:

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

module.exports = withBundleAnalyzer({
  // Your Next.js config
});

Run the build:

ANALYZE=true next build

You'll get an interactive treemap of your bundle size at http://localhost:8888

~ Conclusion

Optimizing performance in a Next.js application doesn’t have to be overwhelming, it’s often about applying the right techniques at the right time and place. By using tools like next/image, next/font, and dynamic imports strategically, you can significantly improve load times, responsiveness, and Core Web Vitals across the board.

Here’s a quick recap of what we covered:

  • Optimized images reduce LCP and improve visual stability.
  • Lazy loading keeps initial loads fast and efficient.
  • Font optimization prevents layout shifts and reduces blocking time.
  • Dynamic imports help split code and load only what’s needed.
  • Bonus tips: compression and bundle analysis

Performance isn't just about passing Lighthouse scores but more about delivering a smoother, faster experience that respects your users time and devices. Whether you're building a side project or scaling a production app, these techniques will help you get the most out of Next.js.

Got any questions or performance tricks of your own? Feel free to share them in the comments!

~ Luan