Before discussing the difference between React.lazy and @loadable/components, let’s talk about what are they and why we need them. Both React.lazy and @loadable/components are mainly being used for the process of Code-Splitting.
Code-Splitting: Code-Splitting is an optimization technique supported by bundlers like Webpack, Rollup, and Browserify (via factor-bundle) which allows us to split our code into multiple bundles or chunks that can be dynamically loaded at runtime on-demand or in parallel.
It’s an efficient way to reduce your application bundle size because we are just “lazy-loading” the things that are currently needed by the user.
Although we have not reduced the overall amount of code in our app this way, we have avoided loading the code that the user may never need. Which in turn reduces the amount of code needed during the initial load and improves the overall loading time of your application dramatically.
React implement Code-Splitting: React supports code splitting out of the box with React.lazy since version 16.6.0.
It has been a common practice since then, to split our components in a React application. However only splitting is not enough, it must be able to wait for the component to be loaded (while showing a fallback UI during loading) and also handle any potential errors.
That’s why we need to use Suspense and Error Boundaries with it.
Javascript
import React, { Suspense } from 'react' ; import MyErrorBoundary from './MyErrorBoundary' ; const SomeComponent = React.lazy(() => import( './SomeComponent' )); const AnotherComponent = React.lazy(() => import( './AnotherComponent' )); const MyComponent = () => ( <div> <MyErrorBoundary> <Suspense fallback={<div>Loading...</div>}> <section> <SomeComponent /> <AnotherComponent /> </section> </Suspense> </MyErrorBoundary> </div> ); |
As we can see from the above example, React.lazy takes a function that is calling a dynamic import() and returns a Promise which resolves to a module with a default export containing a React component. The lazy component is then 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. An Error Boundary is also used to handle any errors that may be caused due to a network issue or similar reason.
@loadable/component: Although React.lazy with Suspense is the recommended solution for Code Splitting, it has some limitations. React.lazy and Suspense are not yet available for Server-Side Rendering. So if you want to do Code-Splitting in a server-rendered app, that’s when @loadable/component comes into play.
Javascript
import loadable from '@loadable/component' const OtherComponent = loadable(() => import( './OtherComponent' )) function MyComponent() { return ( <div> <OtherComponent /> </div> ) } |
So what are some major differences between them?
SSR | Suspense | Library splitting | Full dynamic import | |
React.lazy | ❌ | ✅ | ❌ | ❌ |
@loadbale/component | ✅ | ✅ | ✅ | ✅ |
SSR: @loadable/component provides a complete solution to make Server Side Rendering possible, whereas React.lazy is not an option when it comes to Server Side Rendering. Because Suspense is not available in Server-Side and React.lazy can only work with Suspense.
Suspense: Although Suspense is supported by both by @loadable/component and React.lazy, the difference between them is @loadable/component can also be used without Suspense.
Javascript
const OtherComponent = loadable(() => import( './OtherComponent' )) function MyComponent() { return ( <div> <OtherComponent fallback= {<div>Loading...</div>} /> </div> ) } |
Library splitting: @loadable/component supports library splitting using render props, which is not possible with React.lazy.
Javascript
import loadable from '@loadable/component' const Moment = loadable.lib(() => import( 'moment' )) function FromNow({ date }) { return ( <div> <Moment fallback={date.toLocaleDateString()}> {({ default : moment }) => moment(date).fromNow()} </Moment> </div> ) } |
Full dynamic import: Webpack supports full dynamic imports or aggressive code splitting. You can use them to create a reusable Loadable Component by passing a dynamic value to the dynamic import() function.
Javascript
import loadable from '@loadable/component' const AsyncPage = loadable(props => import(`./${props.page}`)) function MyComponent() { return ( <div> <AsyncPage page= "Home" /> <AsyncPage page= "Contact" /> </div> ) } |