Lazy Loading, Suspense and Error Boundary in React Explained
Lazy loading speeds up page load and saves resources by loading only what’s needed. It improves user experience, especially on slow connections.
Lazy Loading and Suspense
Lazy loading and suspense both could have been separate concepts and thus it seems the explanations should be separate. But in real world apps most of the time you will see they works hand-in-hand. So, this is why I am covering both under one heading.
Lazy loading in React enables you to break your code into smaller chunks, loading only the necessary code for a specific part of your application when it's needed. This reduces the initial load time, making your application more efficient and responsive.
The components you want to be under lazy loading concept are often called dynamic components as they are loaded conditionally at runtime, improving performance by reducing the initial bundle size.
Use React.lazy()
to import the component where you want lazy loading to be enabled. This is where the concept of suspense
comes into play. Because they work hand-in-hand. before I give you a demonstration with an example about the usage of lazy()
, grasp the concept of suspense
.
Suspense
Suspense in React is a component that allows you to handle the loading of asynchronous content, like lazy-loaded components or data, by showing a fallback UI while the content is being loaded. A very common example of using suspense
that shows the usage of suspense
:
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
Here, Suspense
shows the fallback
(like "Loading...") while LazyComponent
is being loaded. Once the component is ready, it replaces the fallback with the actual content.
Lazy Loading Component with Suspense
import React, { Suspense } from 'react';
// Lazy load the component
const LazyComponent = React.lazy(() => import('./LazyComponent'));
function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</div>
);
}
export default App;
Applying React.lazy()
to import a component makes that component lazy loading component. This means the component won’t be loaded during the initial render of the app. Instead, it will only load when it's needed—such as when the user navigates to it or a certain condition is met. This helps improve performance by reducing the amount of code loaded upfront.
Suspense
in Data Fetching
You can use Suspense
to handle various asynchronous operations, such as data fetching or any other task that requires waiting for a resource. By showing fallback content (like a loading spinner) while data is being fetched or an async operation is in progress, Suspense
helps manage the loading state effectively until the process completes, ensuring a smooth user experience.
Here is an example of data fetching:
import React, { Suspense } from 'react';
import fetchData from './dataFetcher'; // Some data-fetching logic
const resource = fetchData(); // Suspends component until data is ready
function DataComponent() {
const data = resource.read(); // Triggers fallback if data isn’t ready
return <div>{data}</div>;
}
function App() {
return (
<Suspense fallback={<div>Loading data...</div>}>
<DataComponent />
</Suspense>
);
}
export default App;
Error Boundary
An Error Boundary in React is a special component that catches JavaScript errors anywhere in its child component tree and prevents them from crashing the entire application. Instead of the whole app breaking, it allows you to display a fallback UI (like an error message) when an error occurs.The most important point here is it catches errors in child component tree.
In React, Error Boundaries must be implemented using class components because currently, there’s no direct way to create an error boundary with functional components using hooks. However, you can use third-party libraries like react-error-boundary
to achieve error boundaries in functional components.
So, install the npm module: npm install react-error-boundary
Here is an example of using Error Boundary in react:
import React from 'react';
import { ErrorBoundary } from 'react-error-boundary';
// Fallback UI for error
function ErrorFallback({ error, resetErrorBoundary }) {
return (
<div role="alert">
<p>Something went wrong:</p>
<pre>{error.message}</pre>
<button onClick={resetErrorBoundary}>Try again</button>
</div>
);
}
// Component that throws an error
function BuggyComponent() {
throw new Error('I crashed!');
return <div>This won't render.</div>;
}
// Main App component with functional error boundary
function App() {
return (
<div>
<h1>My React App</h1>
<ErrorBoundary
FallbackComponent={ErrorFallback}
onReset={() => {
// Reset the state or do any cleanup when retrying
}}
>
<BuggyComponent />
</ErrorBoundary>
</div>
);
}
export default App;
When an error occurs in BuggyComponent, the error boundary catches it and displays the ErrorFallback
Component. ErrorFallback displays the error message and a "Try again" button. The resetErrorBoundary
function is triggered when the user clicks "Try again". resetErrorBoundary
attempts to re-render the child components of the ErrorBoundary
that previously threw an error. If BuggyComponent
is fixed or the error is resolved, it should render successfully on the next attempt.