Performance optimization is the key to having an efficiently functional application and is done by monitoring and analyzing various metrics of an application and identifying ways to improve it. One of the most fundamental and important ways is to lazily load the routes/pages based on user requirements and not fetch the whole chunk at the beginning which can lead to a high load time of the page that results in an increase in user dropoff number.
For best practice, web developers code split large bundles into smaller ones because it enables them to lazy load files on demand and improves the performance of the React application. In this post, we’ll look at how to split our code based on routes using React Router. The idea is simple, don’t download code until the user needs it. The below example is just a simple boilerplate example. In practice, it can be a little more complicated.
Enough of Theory let's dive in...
Install the dependency:
yarn add react-router-dom@6
Wrapping App component:
Import BrowserRouter from react-router-dom near the top of your file and wrap your in a so that all the necessary routing methods are available across your application.
File: main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App';
import './index.css';
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
Creating routes config:
This config will hold all the necessary mapping for our routes like route name, element/component to load lazily, and path. Here we are using useRoutes hook, it is basically the functional equivalent for <Routes>
.
File: routes/index.ts
import { useRoutes } from 'react-router-dom';
import { lazyLoadRoutes } from './LazyLoadRoutes';
export function RouterElement() {
const routes = [
{
path: '/',
name: 'Home',
element: lazyLoadRoutes('Home'),
},
{
path: 'about',
name: 'About',
element: lazyLoadRoutes('About'),
},
{
path: 'services',
name: 'Services',
element: lazyLoadRoutes('Services'),
},
];
return useRoutes(routes);
}
Lazy load component:
As the name says the main purpose of this component is to lazily load the mentioned component. First we lazy load the mentioned component using React.lazy, we wrap around React.Suspense with the fallback text(this can be a loader component as well). Please note that we can tweak the function component so that it takes the path and not just the component name.
File: routes/LazyLoadRoutes.tsx
import { lazy, Suspense } from 'react';
/**
- Lazily load the mentioned component which resides in the page directory
- This method will be used in routes so that the files are loaded only
- When users are on that route
*/
export function lazyLoadRoutes(componentName: string) {
const LazyElement = lazy(() => import(
../pages/</span><span class="p">${</span><span class="nx">componentName</span><span class="p">}</span><span class="s2">.tsx
));
// Wrapping around the suspense component is mandatory
return (
<Suspense fallback="Loading...">
<LazyElement />
</Suspense>
);
}
Navigation bar for route redirection:
Creating navigation route for our pages which are Home, About and Services.
File: App.tsx
import { Link } from 'react-router-dom';
import { RouterElement } from './routes';
import './App.css';
function App() {
return (
<div className="App">
<nav>
<Link to="/">Home</Link>
<Link to="about">About</Link>
<Link to="services">Services</Link>
</nav>
<RouterElement />
</div>
);
}
export default App;
Working Example Link:
As you can see below the chunk for About and Services loads only when we visit that route and the chunk gets cached in the browser so that it is not fetched again in the same session.
Do let me know if there are any other nifty ways in the comments below.
Thanks for your time reading. Drop a few ❤️ or 🦄 if you liked it.
Top comments (1)
Excellent post. This is actually very similar to the approach I've been using. even added some transitions to make the page load smoother, and more digestible. to make it dynamic, I built an endpoint which creates the "pages" array directly from the file directory.