React makes building UI easy. Here’s why.

Ilan Roitlender
Melio’s R&D blog
8 min readMar 20, 2023

--

This year we’re going to celebrate React’s 10 year anniversary! Since it was open-sourced, it has been dominating front-end development. React has become the most used web framework among global software developers since 2021. It makes sense since comparatively, it’s easier to learn, it’s uncomplicated to use, maintain and test, it’s efficient, performs well, and has a rich user interface. According to SimilarTech, 1,345,024 websites have been built with React as of March 2023.

Top websites using React (SimilarTech)

In this blog, we are going to discuss why React has become a popular choice for front-end development and how to prioritize user experience with some practical examples.

I am going to concentrate on two features and ideas to help you improve your user experience and your development process. Let’s dive in.

Lazy loading — how are you?

React lazy loading is a technique that has become increasingly popular among front-end developers in recent years. It involves splitting your application’s code into smaller chunks, or modules, which are loaded on-demand instead of all at once. In this section, we’ll discuss why lazy loading is considered good practice for front-end developers, how it works behind the scenes, and some examples of how it can improve the performance of your application.

First and foremost, lazy loading allows developers to significantly reduce the initial load time of their applications. When a user opens a webpage, the entire codebase of the application must be downloaded and executed by the browser. This process can take several seconds or more, especially if the application is large or complex. By splitting the code into smaller, more manageable modules and loading them only when they are needed, developers can reduce the initial load time of their application and improve the overall user experience.

Behind the scenes, lazy loading works by utilizing the dynamic import() function in JavaScript. This function allows you to load a module asynchronously, meaning that it will not block the main thread of execution while it is being loaded. Instead, the module will be loaded in the background and then executed once it has been fully loaded. This can be especially useful for larger modules or modules that are only needed in certain parts of the application.

Splitting up your app into several chunks comes with several challenges, like deciding what to put in each chunk and when to download it so the user will have a smooth experience.

How to use it

One example of how lazy loading can be used in practice is with React Router. By lazy loading the components that are associated with specific routes, you can ensure that only the code that is needed for each individual page is loaded. This can be a significant improvement over loading all of the components for your entire application upfront, especially if your application has many different routes.

Simple react entry application

In this example, we can see a simple application that renders two routes. The first route that the user will see is the HomePage and only if they will navigate to /customer, the browser will load the rest of the code so the user will see the customer page.

Eager VS lazy loading showcase

Let’s say our requirement is to generate and display a preview of a client receipt PDF file when a user clicks on the button and we know that this feature isn’t used very often by the client side.

For this implementation, we will use some third-party libraries that may increase our bundle size and following this, we may increase the load time for the application.

Third-party libraries usage

Eager loading

The first approach involves importing the component as soon as we load the page — without splitting it into multiple chunks.

Eager loading
bundle size is 585kb

When we load the page for the first time, all js code is downloaded at once even if currently we are not consuming the content.

Lazy loading

Let's make small changes so we can split our application into several chunks. That way, we reduce the initial load time of the page.

Components lazy loading

Let's break it down

const SomeExpensiveComponent = React.lazy(
() => import(/* webpackChunkName: "this_is_expansive" */ 'components/SomeExpensiveComponent')
);

Line 6 we replaced the import with `React.lazy`, by doing so react will not import the component untill it will be used. Webpack supports a special commenting syntax called “magic comments”, which lets you name the different chunks. The bundles we load work fine as is, but they’re not really aptly named. 0.chunk.js and 1.chunk.js might get the job done, but it’s not great for debugging errors in production.

<React.Suspense fallback={<CircularProgress msg="Loading..." />}>
{showExpensiveComponent && <SomeExpensiveComponent />}
</React.Suspense>

Line 28 we’re wrapping the <SomeExpensiveComponent />component in a <Suspense> component and specifying a fallback UI to be shown while the component is being loaded. In this case, we're simply rendering a loader with "Loading..." message.

Simple as that React will handle all asynchronous complexity.

Final result

this_is_expansive.chunk.js has been downloaded only when the user clicks on the download button.
Initial bundle size reduced to 521kb

Let’s use it everywhere (?)

So the short answer is NO. Let me explain: When building a great UI, the application should be responsive and snappy. When we choose to split our application code into multiple little chunks, we need to remember that every time the user clicks and tries to consume the content for the first time, it will lazy load the specific chunk of code. Until it’s done, it will show the default fallback component that we defined in our Suspense component. So in a case where we have multiple chunks, it will show several loaders on the screen.

Spinners everywhere.

How we use it at Melio

Our Tools team at Melio serves a lot of different teams cross-organizationally and each team has its own roles and permissions. For example, Team A will use only specific apps from the Tools product so when the application loads, there is no reason to load all other applications. This will significantly reduce the bundle size and helps to improve overrule user experience.

In conclusion, be lazy (not really).

Lazy loading is a powerful technique that can help improve the performance of your front-end application. By breaking your code into smaller, more manageable chunks and loading them on-demand, you can reduce load times, save bandwidth, better manage the complexity of your code, and provide a better user experience. When used appropriately, lazy loading can be a powerful tool for optimizing the performance of modern web applications.

If you’re a front-end developer, it’s definitely worth considering how you can incorporate lazy loading into your own projects.

Bonus

At the beginning of the blog, I mentioned that I was going to talk about two main features. So now it’s time to talk about error handling in React.

If you are a React developer, you may have come across a situation where your application crashes due to unhandled errors in your components. This can be frustrating for both the developer and the end user. To tackle this problem, React provides a feature called ErrorBoundary.

ErrorBoundary is a React component that catches errors in its child components and displays a fallback UI instead of the crashing component tree. This way, your application can recover gracefully from errors, and the user can continue using it without disruption.

Catch errors like a pro.

Catch, display, and log

One of the main benefits of ErrorBoundary is its ability to catch errors that occur during rendering. When an error is detected, ErrorBoundary will display a fallback UI instead of the crashed component. This not only prevents your application from crashing entirely but also provides a better user experience by showing a message or alternative content. ErrorBoundary can also be used to catch errors in event handlers, lifecycle methods, and other parts of your application.

Display fallback UI for better UX.

This fallback can be applied on smaller components of the UI so in case one of the wrapped components will throw, it will not break the entire UI, and the rest of the page will run normally.

By wrapping each component individually we insure that the page will continue to run even in case one of the component gets a js error.

Let's dive into the code

A class component becomes an error boundary if it defines either (or both) of the lifecycle methods static getDerivedStateFromError() or componentDidCatch(). Use static getDerivedStateFromError() to render a fallback UI after an error has been thrown. Use componentDidCatch() to log error information. [react.org documentation]

In this example, we provide a generic component that can receive a fallback component and in case of an error, will render it instead of this.props.children.

In addition to catching errors, ErrorBoundary also provides a way to log those errors to an API or another external service. This is important for debugging and troubleshooting your application. By logging errors, you can get a better understanding of what’s causing them and take steps to prevent them from happening in the future. This can be especially useful in production environments where it may be difficult to replicate the error.

override componentDidCatch(error, errorInfo) {
this.setState({
hasError: true,
error
});
// API service we use to provide information about the error
logger.error(error, { errorInfo });
}

Great third-party API services that provide log management such as Data-dog and Sentry can save a lot of time and money when issues come up in production.

To use ErrorBoundary in your application, simply wrap the components or parts of your application that you want to protect with the ErrorBoundary component.

In summary, React ErrorBoundary is an essential tool for any engineer building complex React applications. Its ability to catch errors, display fallback UI, and log errors to an API makes it an essential part of your application’s error-handling strategy. By using ErrorBoundary, you can improve the reliability and stability of your application, and provide a better user experience for your users.

Let's wrap it up

Throughout this post, we explored some important concepts of React, such as React.lazy, React.Suspense and ErrorBoundary component. Whether you’re a beginner or an experienced React developer, there is always more to learn, and I encourage you to continue exploring and experimenting with this framework.

Thank you for reading 💜

Visit our career website

--

--