CSR Module Loading in Next.js — because sometimes you just need it to work like React

Don’t get me wrong, Next.js is great. I love it. It works. I don’t need to deal with routing because it’s already baked into the framework. You can write APIs and the inner workings won’t be exposed on the client side. It’s also React without being React.

But sometimes, you just need it to act and behave like React.

Yes, Next.js is React but when I was making my to-do app I found myself in a conundrum. I needed a component to load on the client rather than on the server.

By default, Next.js pre-renders everything on the server and sends it over to the client. After that, it works just like normal React with client-side rendering.

But what if you need something to load on the client? What if you can’t have the component set itself up on the server side and it needs to be done in the browser?

This is where dynamic() importing comes in.

Understanding how Next.js rendering works

By default, Next.js renders the requested page on the server and then sends it to the client. Then React takes with client side rendering for the rest of the site. This process is called React hydration.

So rather than seeing the usual empty Loading... text between a pair of simple <div> , we get a fully formed HTML page followed by React capabilities. It’s great for the bots while remaining fully functional and interactive for users.

In contrast, client side (aka, just React as we know it), works like this:

📌 Step 1: Starts with HTML shell

📌 Step 2: browser fetch JS files containing React

📌 Step 3: content gets rendered and made interactive

There’s a misconception that you have to do one or the other when it comes to client side vs. server side rendering. However, the perk of using Next.js is that you can mix and match rendering methods as needed on that first load.

How dynamic import works

When you compose your view, you’re likely going to import parts of it from within your project. Some are fine to render on the server. Some need to be loaded on the client for whatever reason.

Dynamic importing lets you load on the client side and is downloaded in a separate batch file. The perks of doing this are:

  • reduces the initial page load size
  • is only imported when called in the code
  • only fetched when the component is component is rendered for the first time
  • rendered imports don’t trigger another fetch, so no new server requests once it's loaded

By default, React bundles everything in through static importing. This means that everything is bundled together when it gets delivered to the client. In contrast, dynamic import lets you do the equivalent of ‘lazy loading’ your modules.

Using dynamic loading beyond the first load

Dynamic loading in Next.js isn’t just limited to the first-page load. You can use it in other places to reduce the initial bundle size.

Dynamic loading is often used to load only what you need. It’s a process of deferring the act of loading resources to reduce the size of the initial app. This allows it to be more responsive and feel like it's loading faster for users.

While you can use the defer attribute from JavaScript, dynamic imports is a better solution.

Dynamic importing is not a React-specific or Next.js thing but is a JavaScript feature introduced in ES2020. Next.js does have its own implementation of it, so the syntax is a little bit different from how you’d write it in vanilla JavaScript.

👉 Vanilla JavaScript syntax for dynamic importing

// Statically imported module (compile time) 

import someStaticModule from 'some/module';  

// Dynamically imported module (runtime)
(async () => {     
  const { export1, export2 } = await import('path/to/some/module'); 
})();

How to implement dynamic import in Next.js for CSR

Without further ado, here’s what you’re probably after.

👉 The syntax for dynamic importing in Next.js is this:

const YourComponent = dynamic(() => import(<"./somefile"));

👉 You will also need to import dynamic your export.

import dynamic from "next/dynamic";

👉 Then just use your component as per usual:

<YourComponent />

Dynamic imports with React.lazy

Alternatively, you can also dynamically import using React’s lazy module. It’s a similar concept and syntax structure.

Just a note: Next.js dynamic is an extension of React’s lazy . The major difference is that dynamic also incorporates Suspense so that components can delay hydration until the Suspense boundary is resolved. Suspense lets your component “wait” for code to load or until a certain condition is met.

👉 The syntax for React lazy dynamic importing

const MyLazyComponent = lazy(() => import('path/to/component'));

👉 Remember to import lazy from React, or else it won’t work

import { lazy } from 'react';

👉 Use component as per usual

<MyLazyComponent />

That’s basically it for client-side module loading in Next.js.

I hope you’ve found this piece helpful. Thank you for making it to the end.