DEV Community

Cover image for Introducing dynamic import for React apps and speed up development with code splitting.
palaklive
palaklive

Posted on

Introducing dynamic import for React apps and speed up development with code splitting.

Suppose you have a page that renders different components depending on user input. At the moment, managed to solve the issue I was having and have shown my code below which shows how I solved it:

(Before the introduction of dynamic import)

ComponentList.js

import React from "react";
import ComponentOne from "./ComponentOne";
import ComponentTwo from "./ComponentTwo";
import ComponentThree from "./ComponentThree";
export default function ComponentList({ name }) {

switch (name) {
 case "ComponentOne":
   return <ComponentOne />;
  case "ComponentTwo":
   return <ComponentTwo />;
  case "ComponentThree":
   return <ComponentThree />;
  default:
   return null;
 }
}

Enter fullscreen mode Exit fullscreen mode

Main.js

import React, { Component } from "react";
import ErrorBoundary from "./ErrorBoundary";
import ComponentList from "./ComponentList";
import "./styles.css";

export default class Main extends Component {
  constructor(props) {
   super(props);
   this.state = {name: ""};
   this.handleChange = this.handleChange.bind(this);
  }
  handleChange(event) {
   const {value: name} = event.target;
   this.setState({ name });
  }
  render() {
    const {name} = this.state;
    return (
     <ErrorBoundary>
      <select value={name} onChange={this.handleChange}>
       <option value="">None</option>
       <option value="ComponentOne">Component One</option>
       <option value="ComponentTwo">Component Two</option>
       <option value="ComponentThree">Component Three</option>
     </select>
     <ComponentList name={name} />
    </ErrorBoundary>
   );
  }
}
Enter fullscreen mode Exit fullscreen mode

This method allows me to add/remove components very quickly, as I only need to change one import line at a time.

Bundling

Most React apps will have their files “bundled” using tools like Webpack, Rollup, or Browserify. Bundling is the process of following imported files and merging them into a single file: a “bundle”. This bundle can then be included on a webpage to load an entire app at once.

If you’re using Create React App, Next.js, Gatsby, or a similar tool, you will have a Webpack setup out of the box to bundle your app.

Code-Splitting

Bundling is great, but as your app grows, your bundle will grow too. Especially if you are including large third-party libraries. You need to keep an eye on the code you are including in your bundle so that you don’t accidentally make it so large that your app takes a long time to load.

To avoid winding up with a large bundle, it’s good to get ahead of the problem and start “splitting” your bundle. Code-Splitting is a feature supported by bundlers like Webpack, Rollup, and Browserify (via factor-bundle) which can create multiple bundles that can be dynamically loaded at runtime.

Code-splitting your app can help you “lazy-load” just the things that are currently needed by the user, which can dramatically improve the performance of your app. While you haven’t reduced the overall amount of code in your app, you’ve avoided loading code that the user may never need and reduced the amount of code needed during the initial load.

import()

The best way to introduce code-splitting into your app is through the dynamic import() syntax.

Before:
import { add } from './math';
console.log(add(16, 26));
Enter fullscreen mode Exit fullscreen mode
After:
import("./math").then(math => {
  console.log(math.add(16, 26));
});
Enter fullscreen mode Exit fullscreen mode

When Webpack comes across this syntax, it automatically starts code-splitting your app. If you’re using Create React App, this is already configured for you and you can start using it immediately. It’s also supported out of the box in Next.js.

If you’re setting up Webpack yourself, you’ll probably want to read Webpack’s guide on code splitting. Your Webpack config should look vaguely like this.

When using Babel, you’ll need to make sure that Babel can parse the dynamic import syntax but is not transforming it. For that, you will need @babel/plugin-syntax-dynamic-import.

React.lazy

The React.lazy function lets you render a dynamic import as a regular component.

Before:
import OtherComponent from './OtherComponent';
Enter fullscreen mode Exit fullscreen mode
After:
const OtherComponent = React.lazy(() => 
 import('./OtherComponent'));
Enter fullscreen mode Exit fullscreen mode

This will automatically load the bundle containing the OtherComponent when this component is first rendered.

React.lazy takes a function that must call a dynamic import(). This must return a Promise which resolves to a module with a default export containing a React component.

The lazy component should then be rendered inside a Suspense component, which allows us to show some fallback content (such as a loading indicator) while we’re waiting for the lazy component to load.

Let us now rewrite the logic of the first example.

import React, { Suspense } from "react";
import ErrorBoundary from "./ErrorBoundary";
import "./App.css";

export default function App() {
  const [name, setName] = React.useState("");
  const [DynamicComponent, setDynamicComponent] =
  React.useState(null);
  React.useEffect(() => {
   if (name) {
   const Component = React.lazy(() => import(`./${name}.jsx`));
   return setDynamicComponent(Component);
  }
  return setDynamicComponent(null);
  }, [name]);

  function loadComponent(event) {
   const { value } = event.target;
   setName(value);
  }
  return (
   <Suspense fallback={<div>Loading...</div>}>
     <ErrorBoundary>
       <select value={name} onChange={loadComponent}>
       <option value="">None</option>
       <option value="ComponentOne">Component One</option>
       <option value="ComponentTwo">Component Two</option>
       <option value="ComponentThree">Component Three</option>
       </select>
      {DynamicComponent && <DynamicComponent />}
     </ErrorBoundary>
   </Suspense>
  );
}
Enter fullscreen mode Exit fullscreen mode

From this code sample, we set up our routes using React API, and the ComponentOne, ComponentTwo, and ComponentThree are lazy-loaded. Notice how all the Suspense code encapsulates all the components. This ensures a fallback UI is rendered to the user while the requested components are lazy-loaded.

Because of our setup, webpack chunks our code ahead of time. Consequently, the user receives only the chunks necessary to render a page on demand. For example, when a user visits the homepage, the user receives the ComponentOne.js chunk, and when users visit the shop page, they’ll see the ComponentTwo.js chunk.

Thus, we have significantly reduced our application’s initial load time, even without reducing the amount of code in our app.

Top comments (4)

Collapse
 
vishalpatelvc profile image
Vishal Patel

this is certainly insideful!

Collapse
 
ben profile image
Ben Halpern

Great post!

Collapse
 
prem874 profile image
prem874

Nice!

Collapse
 
muzammilaalpha profile image
muzammilaalpha

good post!!