This library provides a wrapper for @loadable/component, utilizing the IntersectionObserver
API to automatically load component code once the component's "visible". By default, this means once any part of the component has entered the browser's viewport. All for less than 1kb!
It builds upon the great work already done in react-loadable-visibility
, which is an awesome project but seems to be no longer maintained. Further, it is lacking in a few areas that this library attempts to improve. Specifically, at the time of writing, they differ in the following main ways:
- The original library does not expose
@loadable/component
'sload
function. This library does. - The original library does not provide a way to configure the
IntersectionObserver
with your own options (i.e.root
,rootMargin
,threshold
). This library does via exposing an extra function,initObserver
. - The original library applies all props passed to the deferred component to the wrapping
div
, which results in a slew of React console warnings. This library does not apply these props to the wrapper. - The original library does not provide a way to apply styles specifically to the wrapper beyond a
style
orclassName
prop, which also gets applied to the deferred component when loaded. This library does via awrapperCss
prop. - The original library was written to accommodate both
@loadable/component
andreact-loadable
; however,react-loadable
has been deprecated. This library only supports@loadable/component
. - The original library was written in JavaScript. This library is written in TypeScript.
For a super basic demo, you can check out this Stackblitz!
First, install the necessary packages:
npm i react-load-visible @loadable/component
# or...
yarn add react-load-visible @loadable/component
Then, for the default behavior, import the default export and use it just as you would @loadable/component
:
import loadable from 'react-load-visible'
// Example loading component that is rendered while the component's code loads.
import Loading from './Loading';
const Component = loadable(() => import('./Component'), { fallback: <Loading /> })
// Then, somewhere down in your JSX...
// Its code will only be loaded once triggered by the `IntersectionObserver`.
<Component />
Note, you could also apply the fallback to the component itself via a fallback
prop:
import loadable from 'react-load-visible'
// Example loading component that is rendered while the component's code loads.
import Loading from './Loading';
const Component = loadable(() => import('./Component'))
// Then, somewhere down in your JSX...
// Its code will only be loaded once triggered by the `IntersectionObserver`.
<Component fallback={<Loading />} />
If you want to enable (pre)loading, for example on button click, that could look like:
import loadable from 'react-load-visible'
const Component = loadable(() => import('./Component'))
// Then, somewhere down in your JSX...
// Alternatively, could be `Component.preload()`.
<button onClick={() => Component.load()}>Edit User</button>
In general, the loading function and the returned component's API is identical to that of @loadable/component
's. So see their (great) docs for everything you can do!
You may wish to have full control over the instantiated IntersectionObserver
's configuration. You can override the defaults by importing the named export initObserver
and calling it with your desired config:
import { initObserver } from 'react-load-visible'
initObserver({ rootMargin: '500px' })
Note that only a single observer is set up for all deferred components and subsequent calls to initObserver
will not result in separate instances being created.
On the styling side of things, if you want to apply styles specifically to the wrapper component, you can do so by passing a wrapperCss
prop, containing an object of your styles, to the deferred component:
<Component wrapperCss={{ minWidth: '200px', minHeight: '200px' }} />
Note that the wrapper component is only used for the IntersectionObserver
and is completely removed once the deferred component's code loads.
In the case of the code running in an environment that does not provide IntersectionObserver
, such as on the server or in an older browser, the library simply returns the result of @loadable/component
's loadable
call, completely foregoing any IntersectionObserver
related logic.
Note however that you will need to configure @loadable/component
itself correctly to enable SSR.
You will probably want to mock this library when testing components loaded by it. The most straightforward way is to simply call the loader function and return the module. With Jest, that could look like:
import loadable from 'react-load-visible'
jest.mock('react-load-visible')
const mockedLoadable = jest.mocked(loadable)
mockedLoadable.mockImplementation(async (load) => await load())
If using with Webpack, you may receive an error while building that refers to the libraries mjs
file. This means Webpack is processing the mjs
file but doesn't know how to handle it. You can solve this by adding the following to your Webpack config's module rules:
modules: {
rules: [
// ... other rules
{
test: /\.mjs$/,
include: /node_modules/,
type: 'javascript/auto',
},
]
}
If you come across any problems or areas of improvement, please don't hesitate to let me know! Feel free to open an issue or submit a PR.
If submitting a PR, please check tests are still passing after your changes. It's as easy as:
npm test
Licensed under the MIT License, Copyright © 2022-present tmns.
See LICENSE for more information.