Code splitting in React JS
Tags: javascript, react, tutorial, performance
Published: 2023-03-03 14:25:34

When your applications grow, it would be considerable to improve the initial loading speed to make it smaller, and thus avoid the user to leave our app because of having to wait for it to load.

And for that, we can use **code splitting**, which will help us a lot in our applications.

> Note: To understand this post, you must have react router dom bases, because we are going to use a library that we are going to use to explain the code splitting.

&nbsp;

## Table of contents.
> 📌 [Technologies to be used](#1)  
> 📌 [What is code splitting?](#2)  
> 📌 [Creating the project.](#3)  
> 📌 [First steps.](#4)  
> 📌 [Applying code splitting.](#5)  
> 📌 [Conclusion.](#6)  

&nbsp;


## 💢 Technologies to be used. <a id="1"></a>

- React JS v18.2.0
- React router dom v6.7.0
- Vite JS v4.0.0
- TypeScript v4.9.3


## 💢 What is code splitting? <a id="2"></a>

First you need to understand how most frameworks work.
Since many bundle all dependencies into one large file, which makes it easy to add JavaScript to an HTML page. 
**In theory, bundling JavaScript in this way should speed up page loading and reduce the amount of traffic pages have to handle.**

But as an application grows, the size of its bundles also grows and, at some point, its bundles will be so large that they will take a long time to load.

This is where the code splitting technique comes in. **Code splitting** consists of separating the code into several packages or components that can be loaded on demand or in parallel. This means that they are not loaded until they are needed.

*The page still loads the same amount of code, but the difference is because the page may not execute all the code it loads.*

**The benefits of code splitting are:** 

- The speed at which a website loads and displays content becomes faster.
- The interaction time improves.
- The percentage of users who abandon the web page without interacting with it decreases.



## 💢 Creating the project. <a id="3"></a>

We will name the project: **`code-splitting-react`** (optional, you can name it whatever you like).
```bash
npm create vite@latest
```
We create the project with Vite JS and select React with TypeScript.  
Then we run the following command to navigate to the directory just created.

```bash
cd code-splitting-react
```
Then we install the dependencies.
```bash
npm install
```
Then we open the project in a code editor (in my case VS code).
```bash
code .
```

## 💢 First steps. <a id="4"></a>

First we are going to create a few pages. We create the folder **src/pages** and inside we create 6 files. Which will be very simple with very little content.

Notice that the only thing that I place is a div with the name corresponding to the page.

1- Profile.tsx
```tsx
export const Profile = () => {
    return (
        <div>Profile</div>
    )
}
```

Well we do the same with the following 4 pages

2- About.tsx
3- Contact.tsx
4- FAQs.tsx
5- Login.tsx

In this file we will only export each page, that is, we will use it as a barrel file.

6- index.ts
```tsx
export * from './About';
export * from './Contact';
export * from './FAQs';
export * from './Login';
export * from './Profile';
```


**Now let's create an example of how we would normally create an app with routes without applying code splitting.**

We install react router dom
```
npm install react-router-dom
```

Create a folder **src/components** and create a file **NavBar.tsx**.
```tsx
import { Link } from 'react-router-dom';

export const NavBar = () => {
    return (
        <nav>
            <Link to='/home'>Home</Link>
            <Link to='/about'>About</Link>
            <Link to='/contact'>Contact</Link>
            <Link to='/faqs'>FAQs</Link>
        </nav>
    )
}
```

And now inside the **src/App.tsx** file we delete all its content and create a new component.

```tsx
import { BrowserRouter, Navigate } from 'react-router-dom';
import { NavBar } from './components/NavBar';
import { About, Contact, FAQs, Profile, Login } from './pages'

const App = () => {
  return (
    <BrowserRouter>
      <Routes>
        <Route path='profile' element={ <Profile /> } />
        <Route path='contact' element={ <Contact /> } />
        <Route path='about' element={ <About /> } />
        <Route path='faqs' element={ <FAQs /> } />
        <Route path='login' element={ <Login /> } />

        <Route path='/*' element={<Navigate to='/login' replace />} />
      </Routes>
    </BrowserRouter>
  )
}
export default App
```

Now we are going to modify a little, to simulate the private routes. So in the same file we create 2 new components.


In this component we are going to have these routes, that will be the private ones, that is to say that the user will see them until he is authenticated.
Also note that here we are going to show the **<NavBar/>**.

```tsx
export const PrivateRoutes = () => {
  return (
    <>
      <NavBar />
      <Routes>
        <Route path='profile' element={ <Profile /> } />
        <Route path='about'   element={ <About />   } />
        <Route path='contact' element={ <Contact /> } />
        <Route path='faqs'    element={ <FAQs />    } />

        <Route path='/*'      element={<Navigate to='/profile' replace />} />
      </Routes>
    </>
  )
}
```

Then there are the public roads.

```tsx

export const PublicRoutes = () => {
  return (
    <Routes>
      <Route path='login' element={ <Login /> } />

      <Route path='/*' element={<Navigate to='/login' replace />} />
    </Routes>
  )
}
```

Finally we modify the **App** component.
We create a constant to simulate the authentication.
And depending on that constant we will create one route or another.

```tsx
const isAuthenticated = false

const App = () => {
  return (
    <BrowserRouter>
      <Routes>
        {
          (isAuthenticated)
            ? <Route path="/*" element={<PrivateRoutes />} />
            : <Route path="/*" element={<PublicRoutes />} />
        }
      </Routes>
    </BrowserRouter>
  )
}
export default App
```
Maybe you think that because we only render some routes and not others, the routes that are not rendered will not be loaded. But the truth is that even if it is not rendered, either the public or private route on the screen, all the components, all the pages and their css and other packages will always be loaded. 


And you can check it if you run the app, and you go to inspect the page in the **Network** tab and filter it by JS files, you will see how all the pages are loaded, Login.tsx, Profile.tsx, About.tsx, etc.

Maybe now you will not notice any problem in the loading speed, because this app has almost no content. But if you imagine that you have more components or more packages installed, your application would load everything even if you are not using it. 

So let's try to solve this problem with **code splitting**.

## 💢 Applying code splitting. <a id="5"></a>

At this moment we have an "authentication" that will allow us or not to see the private pages.
But previously we saw that even if you are authenticated or not, the pages will always load.

So, what if the user just wants to login and that's it, then there is no need to load the other pages that are private. Or what if the user is already authenticated, there is no need to load the login, until the user decides to log out. 

Well to apply code splitting, let's use:

**React.lazy**. Feature provided directly by React, allows lazy loading of imports. It is a component function, which takes as a parameter another one and finally returns a promise as a result that is expected to be resolved by a React component.

```tsx
const Login = lazy(() => import('./pages/Login'));
```

But, you might get an error, because the lazy function expects the component you want it to return to be a default export.

So we just go to each page, and add its default export.

Take **pages/Login.tsx** as an example. We'll do the same for all the pages.

```tsx
const Login = () => {
    return (
        <div>Login</div>
    )
}
export default Login
```

And then we create the other pages to add the **lazy** function to them.

```tsx
const Profile = lazy(() => import('./pages/Profile'));
const About   = lazy(() => import('./pages/About'  ));
const Contact = lazy(() => import('./pages/Contact'));
const FAQs    = lazy(() => import('./pages/FAQs'   ));
const Login   = lazy(() => import('./pages/Login'  ));
```

Now, let's discuss the imports so that they do not conflict with the new components that we create

```tsx
// import { About, Contact, FAQs, Profile, Login } from './pages'
```

But the final step is still missing, because if we see our app, it will give us an error, and that's because we need a component that suspends the rendering of the component until all its dependencies are loaded.

For it we use the component **<Suspense/>** that React provides us.

```tsx
import { lazy, Suspense } from 'react';

<Route 
    path='login' 
    element={
        <Suspense> 
            <Login />
        </Suspense>
    } 
/>
```
Suspense, also serves as a user interface, since it contains a prop **fallback** that must receive a React component, which will be displayed until the component to which we apply **lazy** has finished loading. So this is a good place to put a loading component.

```tsx
import { lazy, Suspense } from 'react';

<Route 
    path='login' 
    element={
        <Suspense fallback={<>Loading app...</>}> 
            <Login />
        </Suspense>
    } 
/>
```

You will have to add the Suspense to each page that you have applied the lazy function to. And the file would look like this.

```tsx
import { lazy, Suspense } from 'react';
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
import { NavBar } from './components/NavBar';
// import { About, Contact, FAQs, Profile, Login } from './pages'

const Profile = lazy(() => import('./pages/Profile'));
const About = lazy(() => import('./pages/About'));
const Contact = lazy(() => import('./pages/Contact'));
const FAQs = lazy(() => import('./pages/FAQs'));
const Login = lazy(() => import('./pages/Login'));

const isAuthenticated = false

const App = () => {
  return (
    <BrowserRouter>
      <Routes>
        {
          (isAuthenticated)
            ? <Route path="/*" element={<PrivateRoutes />} />
            : <Route path="/*" element={<PublicRoutes />} />
        }
      </Routes>
    </BrowserRouter>
  )
}
export default App

export const PublicRoutes = () => {
  return (
    <Routes>
      <Route path='login' element={<Suspense fallback={<>...</>}> <Login /></Suspense>} />
      <Route path='/*' element={<Navigate to='/login' replace />} />
    </Routes>
  )
}

export const PrivateRoutes = () => {
  return (
    <>
      <NavBar />
      <Routes>
        <Route path='profile' element={<Suspense fallback={<>...</>}> <Profile /></Suspense>} />
        <Route path='about' element={<Suspense fallback={<>...</>}> <About /></Suspense>} />
        <Route path='contact' element={<Suspense fallback={<>...</>}> <Contact /></Suspense>} />
        <Route path='faqs' element={<Suspense fallback={<>...</>}> <FAQs /></Suspense>} />
        <Route path='/*' element={<Navigate to='/profile' replace />} />
      </Routes>
    </>
  )
}
```

You can even create a list with the routes and go through it with a **.map** to avoid having to place the Suspense too much.

But if we now go and look again at the **Network** tab. The truth is that we will not see a big difference in the loading speed, because it is a very small app.

But now, change the authentication to true, so that the private part of the routes is rendered.
```tsx
const isAuthenticated = true
```

Pay attention to the JS files, you will see that not all of them are loaded, only the Profile.tsx because it is the page you are currently viewing.

If you start moving between pages with the Navbar, you will see how they are loaded every page you visit, so they are only loaded until you need them.

**This is how we would apply code splitting to improve the performance of our React applications.**

> I'm not telling you to use lazy on all components either, because it can cause longer load times. **Try to load only components that are not visible in the initial rendering.**


## 💢 Conclusion. <a id="6"></a>

Code splitting is a common practice in large React applications, and the speed increase it provides can determine whether a user continues to use a web application or abandons it, so trimming even fractions of a second could be significant.

I hope I have helped you understand a little more about this topic. If you know of any other different or better way to perform this application feel free to comment. 🙌

> **I invite you to check my portfolio in case you are interested in contacting me for a project!. [Franklin Martinez Lucas](https://franklin-dev.netlify.app/)**

> 🔵 Don't forget to follow me also on twitter: [**@Frankomtz361**](https://twitter.com/Frankomtz361)