Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      Sunshine And March Vibes (2025 Wallpapers Edition)

      June 1, 2025

      The Case For Minimal WordPress Setups: A Contrarian View On Theme Frameworks

      June 1, 2025

      How To Fix Largest Contentful Paint Issues With Subpart Analysis

      June 1, 2025

      How To Prevent WordPress SQL Injection Attacks

      June 1, 2025

      7 MagSafe accessories that I recommend every iPhone user should have

      June 1, 2025

      I replaced my Kindle with an iPad Mini as my ebook reader – 8 reasons why I don’t regret it

      June 1, 2025

      Windows 11 version 25H2: Everything you need to know about Microsoft’s next OS release

      May 31, 2025

      Elden Ring Nightreign already has a duos Seamless Co-op mod from the creator of the beloved original, and it’ll be “expanded on in the future”

      May 31, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      Student Record Android App using SQLite

      June 1, 2025
      Recent

      Student Record Android App using SQLite

      June 1, 2025

      When Array uses less memory than Uint8Array (in V8)

      June 1, 2025

      Laravel 12 Starter Kits: Definite Guide Which to Choose

      June 1, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      Photobooth is photobooth software for the Raspberry Pi and PC

      June 1, 2025
      Recent

      Photobooth is photobooth software for the Raspberry Pi and PC

      June 1, 2025

      Le notizie minori del mondo GNU/Linux e dintorni della settimana nr 22/2025

      June 1, 2025

      Rilasciata PorteuX 2.1: Novità e Approfondimenti sulla Distribuzione GNU/Linux Portatile Basata su Slackware

      June 1, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»Development»How to Optimize Next.js Web Apps for Better Performance

    How to Optimize Next.js Web Apps for Better Performance

    January 2, 2025

    As engineers, we often get so carried away with other aspects of development that we overlook how users perceive and interact with our applications. This oversight can result in users leaving the app almost as soon as they arrive, leading to higher bounce rates and minimal engagement.

    At its core, every business thrives on delivering value to its users. When users are unable to access this value due to poor performance, it ultimately impacts the business’s success. Slow load times, among other factors, frustrate users and drive them away before they even get a chance to engage.

    Optimizing performance is more than just a technical detail – it’s also a critical part of creating a successful application. Without it, even the best features can go unnoticed if users don’t stick around long enough to see them.

    In this article, we’ll explore key approaches to optimize your Next.js application, making it faster and more efficient.

    Table of Contents

    • Building a Performant Application

    • How to Optimize Your Applications

    • Key Techniques To Optimize Performance

      • Using The Next.js Image Component

      • Optimizing Third-Party Scripts with the Next.js Script Component

      • Remove Unused Packages/Dependencies

      • Caching and Incremental Static Regeneration (ISR)

      • Caching Frequently Used Content

      • Font Optimization With next/font

      • Lazy Loading And Code Splitting

      • Lazy Loading in Next.js

      • Code Splitting

    • Conclusion

    Building a Performant Application

    Making your apps more performant means striking the right balance between speed, responsiveness, and efficient use of resources. You should strive to create an application that delivers value and keeps users satisfied.

    Building a performant app is about making sure the app feels smooth and intuitive so that there are no frustrating lags when a user clicks buttons, scrolls, or navigates around. You’ll also want to make sure that data loads or updates without unnecessary delays.

    How to Optimize Your Applications

    The first step in optimizing your application is identifying problem areas. A number of tools and packages can help you analyze your application’s performance effectively. Here’s how you can use them:

    Using npm run build

    When you run npm run build, Next.js creates a production-ready version of your application and gives a detailed breakdown of your pages. This includes:

    • Size: The size of the JavaScript files for each route. Highlighting any routes that are too large and could slow things down. Smaller page sizes generally result in faster load times while large pages might take longer to download, especially for users with slower network connections.

    • First Load Js: This column provides information about the total amount of JavaScript the browser needs to download and execute to fully render the page for the first time. Large First Load JS values

      cause Slower Time-to-Interactive (TTI).

    Running this command produces an analysis like below:

    Example result of running npm run build

    Using @next/bundle-analyzer

    The bundle analyzer is a package provided by Next.js to analyze the size of JavaScript bundles by providing a visual representation of the application’s module and dependencies. Here’s how to use the package:

    First, install the package by running this command:

    npm install @next/bundle-analyzer
    

    Or you can use yarn:

    yarn add @next/bundle-analyzer
    

    Then add the @next/bundle-analyzer configuration to your next.config.js file:

    const withBundleAnalyzer = require('@next/bundle-analyzer')({
      enabled: process.env.ANALYZE === 'true',
    });
    
    module.exports = withBundleAnalyzer({
      // other Next.js config options here
    });
    

    To analyze your application bundles while generating a production build, run the following command:

    ANALYZE=true npm run build
    

    For a step-by-step guide on how to use the bundle analyzer effectively, check out this detailed video tutorial

    Browser tools

    Finally, modern browsers, including Google Chrome, Firefox, and Edge, offer powerful tools to analyze and improve your application’s performance. Features like the Performance Tab help you record and visualize how your application runs, pinpointing issues like slow rendering or long tasks.

    You can also use tools like Lighthouse (available in Chrome and Edge) to generate automated audits, highlighting problems such as large assets and unoptimized resources.

    To access the Lighthouse and Performance tabs:

    1. Open your browser’s developer tools by right-clicking anywhere on the browser and selecting the Inspect option or pressing Command + Option + I (on Mac) or Ctrl + Shift + I (on Windows).

    2. Look at the top menu in the developer tools.

    3. If you don’t see the Lighthouse or Performance tabs right away, click the double right arrow (>>) to reveal hidden tabs.

    4. Select the desired tab to start analyzing performance or generating a Lighthouse report.

    Here is an example of a generated audit in the Performance tab on Chrome

    image of the performance tab on chrome browser

    Here’s another image showing the generated audit by lighthouse

    dfde608b-eeb7-443d-81c6-56ff2a6dd92b

    Key Techniques to Optimize Performance

    1.) Using The Next.js Image Component

    Images often account for the largest portion of page weight, directly affecting load times and user experience. Large images slow down rendering and ultimately, increase bandwidth usage.

    Next.js has a built-in Image component that automatically optimizes images, making it very useful for web performance. It takes care of resizing, lazy loading, and format optimization, so images are served in the most performant format (like .WebP) when the browser supports it.

    import Image from 'next/image';
    
        <Image
          src="/house.jpg"
          alt="House Image"
          width={700}
          height={500}
          priority={false} // Lazy loads the image by default
        />
    

    In the snippet above,

    • src="/house.jpg": This points to the image file’s location, which is in the public folder. Images in the /public directory are served statically, so you don’t need extra configuration.

    • alt="House Image": The alt text (just like in the native HTML image element) provides a description of the image, which is great for accessibility (like screen readers) and also helps with SEO.

    • width & height: By explicitly setting the width and height, Next.js can calculate the space the image will occupy on the page before it loads. This prevents the page layout from shifting as the image loads, which improves user experience and boosts performance metrics like Cumulative Layout Shift (as shown in the image above).

    • priority={false}: This ensures the image will only load when it’s near the user’s viewport conserving the bandwidth and improving page load times for non-critical images. However, for important images that should load immediately (like those visible as soon as the page opens), you can set priority={true} to bypass lazy loading and ensure the image loads as quickly as possible.

    One of the key advantages of the Next.js Image component is its built-in lazy loading feature. This means that images won’t be loaded until they are actually needed (when they enter the viewport). By only loading images that are about to be viewed, performance is improved and pages can load faster, even with many high-quality images.

    2.) Optimizing Third-Party Scripts with the Next.js Script Component

    Third-party scripts, such as analytics tools or advertising networks, can heavily affect your application’s performance if not properly managed. Next.js has a Script component that makes it easy to load scripts efficiently, giving you control over how and when they load.

    The Script component allows you to define a loading strategy for scripts, determining when and how they are fetched and executed. By prioritizing or deferring scripts based on their importance, you can improve the overall performance and user experience of your application.

    • beforeInteractive: Use this strategy for scripts that must load before the page becomes interactive, like essential analytics or monitoring tools.

    • afterInteractive: When you use this strategy, the script loads after the page becomes interactive, which is the default behavior. This is ideal for scripts that add functionality but aren’t essential for initial rendering.

    • lazyOnload: Defers loading the script until all other page resources have finished loading. This is perfect for non-essential scripts like ads or social media widgets.

    <Script src="https://example.com/non-essential.js" strategy="lazyOnload" /> //Pass the strategy as a prop to the component
    

    By leveraging the Next.js Script component, you can prevent scripts from blocking critical rendering, reducing load times and improve Time to Interactive (TTI).

    3.) Remove Unused Packages/Dependencies

    Over time, as you build and maintain your project, unused dependencies can pile up in your codebase. These unnecessary packages increase the size of your project, slow down installation times, and make the code harder to maintain. Cleaning up these unused dependencies is essential for optimizing your application’s performance and keeping your codebase clean.

    The depcheck tool is a great way to identify and remove unused dependencies from your project. It analyzes your package.json and the project files to find unused dependencies, unused devDependencies, and missing dependencies.

    You can run a depcheck like this:

    npx depcheck
    

    After identifying the unused dependencies, you can remove them by running:

    npm uninstall <package-name>
    

    or with yarn:

    yarn remove <package-name>
    

    Regularly running depcheck is a simple yet effective way to keep your project clean and efficient.

    4.) Caching and Incremental Static Regeneration (ISR)

    When you find yourself running the same calculations or database queries repeatedly, you should consider caching. It’s a simple yet powerful way to boost your web application’s performance, especially for content that doesn’t change often. By storing frequently accessed data in a cache, you can avoid unnecessary processing and speed up load times.

    In Next.js, you can take this a step further with Incremental Static Regeneration (ISR), which lets you serve static content instantly while keeping it fresh behind the scenes.

    Incremental Static Regeneration (ISR) in Next.js lets you update static pages without rebuilding the whole site. Here’s how it works:

    1. Build time generation: ISR generates pages when the site is built.

    2. Caching: It stores the pages so they load quickly when users visit.

    3. Background updates: When content changes, ISR updates the pages behind the scenes without affecting users.

    4. Dynamic updates: It combines the fast loading of static pages with the ability to update content regularly.

    export async function getStaticProps() {
    
      const data = await fetchData();
    
      return {
        props: { data },
        //regenerate the page every 20 seconds.
        revalidate: 20,
      };
    }
    
    //pre-render the page as static content
    function MyPage({ data }) {
      return (
        <div>
          <h1>My Page</h1>
          <p>{data}</p>
        </div>
      );
    }
    
    export default MyPage;
    

    Caching Frequently Used Content

    For websites with pages that get a lot of visitors, like product listings or blog posts, it’s important to keep the content fast and up-to-date.

    Caching helps achieve this by saving a copy of the page so it doesn’t need to be created from scratch each time someone visits. The browser or server will store this cached page for a set amount of time, which is controlled by caching headers. Meanwhile, ISR (Incremental Static Regeneration) ensures that the page can be updated in the background when necessary, without needing to rebuild the entire site.

    In applications with lots of data, caching can also speed up the process by storing API responses. This way, when users request the same data again, they can get it quickly from the cache instead of waiting for it to be fetched anew. Tools like Vercel and Content Delivery Networks (CDNs) help by storing these cached pages in multiple locations around the world, so visitors can access them faster.

    export async function getStaticProps() {
      const data = await fetchData();
    
      return {
        props: { data },
        // Regenerate page at most once every 30 seconds
        revalidate: 30,
        // Cache for 1 hour at the CDN level
        headers: {
          'Cache-Control': 'public, max-age=3600, must-revalidate',
        },
      };
    }
    

    Here, the page regenerates every 30 seconds and is cached at the CDN level for one hour. The Cache-Control header tells the CDN and browser to cache the page for 1 hour and revalidate it afterward.

    For a deeper dive into caching and its role in web performance, check out this insightful freeCodeCamp article on Caching vs. Content Delivery Networks.

    5.) Font Optimization With next/font

    The next/font module in Next.js automatically handles font loading for improved performance, so you don’t need to manually configure or use extra libraries. It loads only the essential parts of the font, which results in faster page load times.

    To further reduce the font file size, you can provide the subsets array which ensures fewer bytes are transferred and pages load quickly.

    Here’s how it works:

    • Automatic font loading: The module optimizes font loading automatically, making sure fonts are served in the most efficient way, improving performance without extra effort.

    • Subsetting fonts: You can specify the exact font characters needed for your app.

    • Font display strategy: The font-display strategy determines how text is shown to the user while fonts are loading. Next.js typically uses the swap strategy by default, but you can manually configure it if necessary. The most common strategies are swap fallback optional and block.

    •   import { Inter } from 'next/font/google'
      
        const inter = Inter({
          subsets: ['latin', 'latin-ext'], // Load only the Latin and extended Latin subsets
          weight: '400', // Choose the specific weight you need
          style: 'normal', // Specify the style if needed
        })
      
        export default function Page() {
          return <div className={inter.className}>Hello World</div>
        }
      

    The snippet above uses the Next.js built-in tool for Google Fonts. Instead of adding the font link in your HTML or using a third-party library, you can import it directly like this for ease and efficiency.s

    • subsets: Tells the app to load only the characters needed. Skipping other character sets like Cyrillic (used in Russian) or Greek, avoids downloading extra, unnecessary data, which keeps your app lightweight and faster to load.

    • weight: Instead of loading all font weights (e.g., Bold, Light), you only bring in Regular (400). This reduces the overall size.

    • style: Stick with the standard style (no fancy italics). This also trims down what’s downloaded.

    6.) Lazy Loading and Code Splitting

    When building web apps, you want to make sure your users don’t wait too long for your pages to load. A big part of this involves reducing how much JavaScript is loaded when the page first opens. Two techniques that help with this are lazy loading and code splitting, both of which Next.js makes easy to use.

    Lazy Loading in Next.js

    Think of lazy loading like waiting to download a movie only when you decide to watch it. Imagine you have a large component like a chart or a map that users only see after interacting with a page. Instead of loading it upfront, you can tell Next.js to load it only when it’s needed using next/dynamic.

    Code Splitting in Next.js

    Code splitting breaks your JavaScript into smaller pieces (called bundles), so users only load what’s necessary. For example, if a user visits your homepage, there’s no need to load JavaScript for other pages like “About Us” or “Dashboard”. It typically happens during the build process or dynamically at runtime.

    import dynamic from 'next/dynamic'
    
    // Load HeavyComponent only when it’s rendered
    const HeavyComponent = dynamic(() => import('./HeavyComponent'), { ssr: false })
    
    export default function Home() {
      return (
        <div>
          <h1>Welcome Home!</h1>
          <HeavyComponent /> {/* This loads only when rendered */}
        </div>
      )
    }
    

    In the above code, dynamic dynamically imports the component only when needed. ssr: false disables server-side rendering for the component, which can save resources if the component doesn’t need to be pre-rendered.

    Next.js automatically splits code by page, meaning each page only loads the necessary JavaScript when accessed, improving load times. For more granular control, next/dynamic allows you to dynamically import specific components, ensuring they are loaded lazily only when needed. While Next.js handles page-level code splitting by default, using next/dynamic gives you the flexibility to apply component-level splitting, optimizing resource loading and enhancing performance.

    Conclusion

    Creating a high-performance application is a very important aspect of any business. A faster and more efficient application enhances user engagement, lowers bounce rates, and boosts SEO rankings, which all contribute to business growth and customer satisfaction.

    By utilizing these techniques we discussed in this guide, you can provide a smooth user experience while maintaining optimal efficiency behind the scenes.

    Remember, every second saved in load time translates to happier users and, ultimately, better business outcomes.

    Thank you for reading!

    Want to connect with me?

    • Twitter / X: @timi471

    • Linkedin: Ayantunji Timilehin

    • Email: ayantunjitimilehin@gmail.com

    References

    • Next.Js Documentation

    • Caching-vs-content-delivery-network

    • Using next/bundle-analyzer

    • Cumulative layout shift

    Source: freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleHow to Make Learning to Code Easier by Focusing on the Fundamentals
    Next Article Error’d: Monkeys

    Related Posts

    Artificial Intelligence

    Markus Buehler receives 2025 Washington Award

    June 1, 2025
    Artificial Intelligence

    LWiAI Podcast #201 – GPT 4.5, Sonnet 3.7, Grok 3, Phi 4

    June 1, 2025
    Leave A Reply Cancel Reply

    Hostinger

    Continue Reading

    CVE-2025-43967 – Libheif NULL Pointer Dereference Vulnerability

    Common Vulnerabilities and Exposures (CVEs)

    The Thunderbird email client finally landed on Android, and it was worth the wait

    Development

    I work from home – this lapdesk turned my couch into my office (and I can’t go back)

    News & Updates

    Handling Complex Test Scenarios with Selenium and Pytest: Advanced Techniques

    Development

    Highlights

    Facebook privacy – why statements about copyright don’t do anything

    April 9, 2025

    Facebook users around the world have reported the return of the network’s longer-lasting hoaxes -…

    Activating plugin on Appium Server2.0

    June 23, 2024

    New Helldivers 2 Major Order presents a dilemma between saving a new stratagem or ‘moderately feeble young adults,’ but players boldly reply: “Why not save both?”

    February 26, 2025

    OpenAI safety researcher announces their departure over safety concerns while Anthropic CEO claims AI will extend human life “to 150 years”

    January 31, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.