# Code Splitting and Bundling in Next.js

When building web applications, performance is everything. Users expect apps to load fast and feel responsive. That’s where **code splitting** and **bundling** come into play. These techniques ensure your app only loads the JavaScript it needs for a given page or feature, reducing load times and improving overall performance.

In this blog, we’ll explore how **Next.js** handles code splitting and bundling out of the box and how you, as a developer, can take it to the next level.

---

## What Are Code Splitting and Bundling?

### **Code Splitting**

Code splitting breaks your app into smaller pieces (chunks) of JavaScript that are loaded on demand. Instead of sending one large file to the browser, you send only the necessary code for the specific page or feature being accessed.

### **Bundling**

Bundling combines your app’s files into one or more JavaScript files. This process groups related files and dependencies, ensuring the app runs efficiently in the browser.

---

## How Next.js Handles Code Splitting and Bundling

One of the great things about **Next.js** is that it automatically optimizes your app with **built-in code splitting and bundling**. Let’s break it down:

### 1\. **Per-Page Bundles**

Next.js creates separate bundles for each page in your app. When a user visits a page, only the JavaScript required for that page is loaded. For example:

* Visiting `/about` only loads the code for the `pages/about.js` file and its dependencies.
    
* The rest of the app’s code stays untouched until the user navigates to another page.
    

This means smaller bundles and faster initial loads.

---

### 2\. **Shared Bundles**

If multiple pages in your app use the same dependencies (e.g., React or a UI library), Next.js automatically extracts these into **shared bundles**. Shared bundles are cached by the browser, so users don’t need to download them multiple times.

---

### 3\. **Dynamic Imports**

Next.js supports **dynamic imports** for code splitting at the component level. This means you can load components only when they’re needed, not as part of the initial bundle.

Here’s an example:

```typescript
import dynamic from 'next/dynamic';

const DynamicComponent = dynamic(() => import('../components/HeavyComponent'));

export default function HomePage() {
  return (
    <div>
      <h1>Welcome to My App</h1>
      <DynamicComponent />
    </div>
  );
}
```

In this case, `HeavyComponent` is only loaded when it’s rendered, reducing the initial load size.

---

## Improving Code Splitting and Bundling in Next.js

While Next.js does a lot for you automatically, there are additional techniques you can use to further optimize your app.

### 1\. **Analyze Your Bundle**

Next.js provides a built-in tool to analyze your app’s bundle size. You can use the **Webpack Bundle Analyzer** plugin to visualize your app’s bundles and identify opportunities to reduce their size.

#### How to Enable the Analyzer:

Install the analyzer plugin:

```bash
npm install @next/bundle-analyzer
```

Update your `next.config.js`:

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

module.exports = withBundleAnalyzer({});
```

Run the build with the analyzer:

```bash
ANALYZE=true npm run build
```

This generates a visual report of your app’s bundles, helping you spot large dependencies or unnecessary code.

---

### 2\. **Tree Shaking**

Tree shaking is a process that removes unused code from your bundles. Next.js automatically performs tree shaking for libraries that support ES Modules. To make the most of this:

* Use modern libraries that support ES Modules.
    
* Avoid importing everything from a library. Instead, import only what you need.
    

#### Example:

Instead of:

```typescript
import _ from 'lodash';
```

Use:

```typescript
import debounce from 'lodash/debounce';
```

---

### 3\. **Dynamic Imports with SSR**

Dynamic imports can be further optimized by disabling server-side rendering (SSR) for non-critical components. This can save server resources and improve client-side performance.

#### Example:

```typescript
import dynamic from 'next/dynamic';

const NoSSRComponent = dynamic(() => import('../components/NoSSRComponent'), { ssr: false });

export default function Page() {
  return <NoSSRComponent />;
}
```

Here, `NoSSRComponent` is loaded only on the client, not during server rendering.

---

### 4\. **Code Splitting CSS**

If you’re using CSS in JS (e.g., Tailwind CSS or styled-components), Next.js automatically extracts critical CSS for each page. However, you can optimize further by:

* Removing unused CSS (using tools like `purgecss`).
    
* Splitting CSS files into smaller chunks for large stylesheets.
    

---

### 5\. **Optimize Images**

While not directly related to JavaScript bundling, optimizing images can significantly improve perceived performance. Next.js provides the `next/image` component, which automatically optimizes images for lazy loading and responsive sizes.

---

## Real-World Example: Optimized Next.js App

Let’s combine these techniques in a sample app:

```typescript
import dynamic from 'next/dynamic';
import Image from 'next/image';

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

export default function HomePage() {
  return (
    <div>
      <h1>Welcome to Optimized App</h1>

      {/* Lazy Loaded Image */}
      <Image
        src="/images/optimized.jpg"
        alt="Optimized"
        width={500}
        height={300}
      />

      {/* Dynamically Imported Component */}
      <DynamicComponent />
    </div>
  );
}
```

---

## Key Takeaways

1. **Next.js Automatically Optimizes**:
    
    * Per-page and shared bundles.
        
    * Tree shaking to remove unused code.
        
2. **You Can Improve Further**:
    
    * Analyze your bundle using tools like `@next/bundle-analyzer`.
        
    * Use dynamic imports to split code at the component level.
        
    * Optimize dependencies and CSS.
        
3. **Test Performance**: Use tools like **Lighthouse** or **WebPageTest** to measure your app’s performance before and after optimization.
    

---

By leveraging Next.js’s built-in features and combining them with these techniques, you can create highly optimized web applications that load faster, perform better, and keep your users happy. 🚀

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1737896736353/65d5a779-f7ce-4fa8-9dad-6fa8aac5539e.jpeg align="center")
