Next.js - how to reduce bundle size?

Learn 4 steps to reduce bundle size in Next.js for improved web performance.
Published 2023-09-22 6 min read
Next.js - how to reduce bundle size?

Next.js is cool and makes web development more straightforward, but let’s be honest - it’s not perfect when it comes to bundle size sent to the client. Barebone installation of Next.js sends around 80kB of JavaScript to the client (gzipped), with no additional dependencies or fancy components. It’s easy to go over double that, which requires parsing and running this code, resulting in poor performance, especially on the first load. Ask me how I know…

Let’s review the basic process for analyzing and reducing the Next.js bundle size. We’ll begin with the analysis and then go through some actionable items that can reduce your bundle size and then we’ll fix you with some recommendations for the future. Also, I’ll share my experience with some libraries I recommend avoiding in your project. 

 

Step 1: Analyze build output

The first tool is the Next.js itself. Next.js does a good job of informing you what is the total bundle size. It presents it to you when you build your application via next build command:

Route (app) Size First Load JS
┌ ○ / 5.17 kB 84.3 kB
└ ○ /_not-found 878 B 80.1 kB
+ First Load JS shared by all 79.2 kB
  ├ chunks/864-4a979332dff86894.js 26.5 kB
  ├ chunks/fd9d1056-e4abb10dc68a7173.js 50.8 kB
  ├ chunks/main-app-c991a9de9e76ebd4.js 220 B
  └ chunks/webpack-bba7619efc7529ac.js 1.64 kB

 

We are of course interested in First Load JS. It’s a minified and gzipped size. The provided output is from a barebone Next.js app, so as you can see Next, by default has a bundle size of around 80kB.

This method is not precise, but it’s easy to check the bundle size for each route in your application. If you want more details, you should go to step 2.

 

Step 2: Do a real bundle size analysis with @next/bundle-analyzer

It’s time to go deeper and visualize your bundle. Vercel shared a great tool to do just that. It’s called a Bundle Analyzer. To install it go for (depending on your package manager):

npm install @next/bundle-analyzer
yarn add @next/bundle-analyzer
bun install @next/bundle-analyzer

Then go to your next.config.js and wrap your configuration with the following:

// your standard config
const nextConfig = {...}



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

module.exports = withBundleAnalyzer(nextConfig)

 

Then run the build command, like this:

ANALYZE=true yarn build

of course, you would have to modify the command with different package managers. Running this command will open a few tabs with treemaps, one of which will be the client bundle chart. This is an interesting one. Here you can see what each chunk exactly contains and how heavy each subpart is. Here’s an example from a test app I created when I compared the performance of REST, GraphQL, and trpc in Next.js.

@next/bundle-analyzer treemap

For each dependency you should ask yourself - do I *really* need it? If you never analyzed your bundle, it would be a pretty safe bet that at least a few dependencies could be dropped.

It may happen that you’ll find a sizable package that you don’t remember adding or that is not present in the package.json as a dependency. If you use yarn you can use yarn why wtf-package to find out what other package depends on it. Then it’ll be easier to decide to do something about it.

 

Step 3. Common ways to decrease Next.js bundle size

Of course, at first, your goal would be to remove completely unnecessary packages found in a previous step.

Next concentrate on packages that in theory are used somehow, but are not that critical for the application. In this category, I would mention lodash, which has no business in most front-ends, especially if you use one or two methods from there.

Then you should ask yourself if you can swap bulky packages for smaller ones. The first one that can save you around 50kB is Preact, a lightweight React alternative. If you use Preact instead of React, the barebones Next.js bundle comes at around 35kB - not bad. Note that most apps will work with Preact as a drop-in replacement for React, but some won’t because there are some differences between the two (that’s why there is such a big difference in size).

React ecosystem is huge and for almost everything, you’ll find a dozen alternatives. Leverage that! For example, if you use GraphQL a popular option is apollo/client, but it’s a hefty package, urql is much leaner. Although migrating from one to another might be not straightforward. It’ll be the case for most packages.

Another common way to optimize a bundle is not to decrease its size, but to defer loading some parts of it. Some parts of a website are not needed from the very beginning and can be lazily loaded. Next.js provides a great tool for that, called Dynamic Imports. Another technique is to use React.suspense. Read the Next.js guide on lazy loading ​for more details.

 

Step 4. Housekeeping, to keep bundle size small

Sure, you can rinse and repeat steps 1 and 2 regularly, and you should! But there are at least two things during development that will make a difference.

There is a cool extension for VS Code called Import Cost, it automatically counts the cost of each import in the current file. It looks something like this:

Import Cost visualisation

Some users are reporting heavy lagging because of this extension, but I haven’t encountered any issues. I guess you can check how it works for you.

If using VS Code extensions is not your thing you definitely should check out https://bundlephobia.com/. This website tracks bundle sizes for popular npm packages. Make sure to visit it and do your research before adding a new package.

Keeping bundle size in check is about paying attention. It’s much easier to achieve it when you keep it in mind right from the start, but it’s always. possible to get some ground back 

 

In conclusion

Bundle size for the sake of it is probably not worth it. At the end of the day, it’s about the user experience. The bigger the bundle size is, the more this experience degrades, especially on low-end devices and in poor networking conditions. That’s why we do it. Regardless of that, all frontend devs should be aware of how much JavaScript their app sends to the client. Not knowing and monitoring that is a sign of sloppy development practices.

#nextjs #performance