devugur

Code splitting techniques in Javascript and React

19 Dec 2023

Photo by <a href="https://unsplash.com/@willfrancis?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Will Francis</a> on <a href="https://unsplash.com/photos/green-grass-near-the-gray-road-Rm3nWQiDTzg?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash">Unsplash</a>
Photo by Will Francis on Unsplash

What is code splitting and why do you need it?

Code splitting is a technique used in software development to break a large code into small pieces that can be loaded independently. Loading smaller chunks of code when needed reduces the initial load time of an application. That makes your app faster. Faster initial page load, saving bandwidth and even better caching.

It offers numerous advantages and is typically straightforward to incorporate. It's a valuable asset to include in your development repertoire.

Let's start by learning the basics!

Dynamic imports in Javascript

With the introduction of dynamic imports in JavaScript (using import()), you can asynchronously load modules or chunks of code when needed. This allows for on-demand loading, improving performance by fetching code only when required.

Check this code:

import userLogger from './userLogger';
import vehicleLogger from './vehicleLogger';

const item = {
  name: 'Josh',
  type: 'vehicle',
};

(() => {
  let logger;
  if (item.type === 'vehicle') {
    logger = vehicleLogger;
  } else {
    logger = userLogger;
  }

  logger.log();
})();

We are importing two different loggers but we are using only one of them depending on the item.type. Which means one of them is important even though we don't need it at all. Imagine those logger files are huge. We are wasting lots of resources.

We can fix it by using import().

const item = {
  name: 'Josh',
  type: 'vehicle',
};

(async () => {
  let logger;
  if (item.type === 'vehicle') {
    import('./vehicleLogger').then((module) => {
      logger = module.default;
    });
  } else {
    import('./userLogger').then((module) => {
      logger = module.default;
    });
  }

  logger.log();
})();

Using React.lazy and Suspense

Using import() statement is cool, but React provides us a helper function to load components lazyly: React.lazy.

import { lazy, Suspense } from 'react';

const profileTypes = ['admin', 'helpdesk', 'regular'] as const;

type ProfileType = (typeof profileTypes)[number];

function getProfilePage(userType: ProfileType) {
  switch (userType) {
    case 'admin':
      return lazy(() => import('./AdminProfile'));
    default:
      return lazy(() => import('./RegularProfile'));
  }
}

interface User {
  type: ProfileType;
  name: string;
}

const user: User = {
  name: 'ugur',
  type: 'admin',
};

export default function ProfilePage() {
  const Profile = getProfilePage(user.type);

  return (
    <div className="pt-40">
      <h1>Profile</h1>
      <Suspense fallback={<div>loading...</div>}>
        <Profile name={user.name} />
      </Suspense>
    </div>
  );
}

In this way, you will never download extra javascript files for your regular users.

Suspense will render a fallback UI while the actual javascript has been loading. Once the page is loaded you won't see the fallback UI again unless you reload the page.

You can also take advantage of react-router to make route based code splitting.

import { Route } from 'react-router-dom';

let routes = createRoutesFromElements(
  <Route path="/" element={<Layout />}>
    <Route path="invoices" lazy={() => import('./invoices')} />
    <Route path="orders" lazy={() => import('./orders')} />
  </Route>
);

For more on that check official docs.

Conclusion

Implementing code splitting requires additional consideration because it involves making decisions on where and what parts of the code to split. Frequently, ideal splitting points emerge within the routing structure. Alternatively, you might realize that certain functionalities are necessary only when specific features are accessed. A prime illustration of this scenario is evident in tasks like charting and authorization.

Extra Reading