Skip to content

A wrapper for @loadable/component that uses IntersectionObserver to trigger component load.

License

Notifications You must be signed in to change notification settings

tmns/react-load-visible

Repository files navigation

react-load-visible ⚛️ 🌀 👀

Intro

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:

For a super basic demo, you can check out this Stackblitz!

Getting started

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!

Customizing

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.

What about SSR?

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.

Tips

Test environment

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())

Webpack

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',
    },
  ]
}

Contributing

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

License

Licensed under the MIT License, Copyright © 2022-present tmns.

See LICENSE for more information.