react-scroll-blocking-layers
v1.1.2
Published
Hooks for managing layers that block background scrolling in React
Downloads
162
Maintainers
Readme
react-scroll-blocking-layers
This packages helps managing UI layers that block background scrolling. It is primarily used for modals and overlays, but can also come in handy with multi-layer layouts.
It supports nested layers and viewport size boundaries which can be useful for layers that are only visible at a certain screen size. It works with all major browsers and devices, including iOS. It's comparably small at only 1.4kb minified & gzipped.
Background
Overlays and Modals are very common components that can be found in almost every modern web application However, implementing them in a user-friendly, accessible way is not at all straightforward.
When it comes to scrolling, most users expect to focus only on the actual layer, but the browser will still scroll the main scrolling element, usually the body
, once the end of the layer content is reached.
To prevent that, we usually block that background scrolling as soon as an additional layer becomes visible. One might think a simple overflow: hidden
might be enough here, but it turns out that it is not that simple.
There are a lot of solutions and packages out there that aim to block scrolling, but most of them either don't work in every environment or are too complex and too much overhead for modern browsers. That's why we built our own solution and eventually extracted it from our codebase to open source it.
Check out Robin Weser's blog post for a full deep-dive into the background and problems.
Example
We have a tiny example application that you can use to test it. You can find it under https://react-scroll-blocking-layers.vercel.app
Installation
# pnpm
pnpm add react-scroll-blocking-layers
# yarn
yarn add react-scroll-blocking-layers
# npm
npm i --save react-scroll-blocking-layers
Documentation
The package exposes three hooks and a context provider component. All the scroll blocking and counting logic is build into a combination of those.
LayerContextProvider
The context provider accepts no props apart from children
.
It is used to keep track of the number of active layers in order to apply scroll blocking correctly for multiple layers.
It must wrap your whole application to reliably work.
Tip for Next.js users: You want this component to be part of your
_app.js
ideally.
Example
import { LayerContextProvider } from 'react-scroll-blocking-layers'
import App from './App'
export default function Page() {
return (
<LayerContextProvider>
<App />
</LayerContextProvider>
)
}
useLayer
This hook provides the basic mechanics of toggling a scroll blocking layer. The API interface is identical to React's useState. It takes an initial boolean visible state and returns an array with a pair of state and an updater
Parameters
| Parameter | Type | Default | Description |
| -------------- | --------- | ------- | ----------------------------------------------------- |
| initialVisible | boolean
| false
| Whether the layer is visible on initial render or not |
Example
import { useLayer } from 'react-scroll-blocking-layers'
import Modal from './Modal'
function Info() {
const [modalVisible, setModalVisible] = useLayer()
return (
<>
{modalVisible && <Modal onClose={() => setModalVisible(false)} />}
<button onClick={() => setModalVisible(true)}>Open Modal</button>
</>
)
}
useLayerWithSizeConstraints
This hook is very similar to useLayer. In fact, it uses useLayer under the hood.
The only difference is that it adds support for viewport constraints. It accepts a maxWidth
prop that is used to decide if the layer is shown or not. It adds a resize
event listener to track the viewport size.
If you wonder why there is no
maxHeight
respectively: We figured that from a UX perspective there is no such use case in modern web applications.
Parameters
| Parameter | Type | Default | Description |
| -------------- | --------- | ------- | ------------------------------------------------------- |
| maxWidth | number
| | The maxixum width at which the layer can be visible |
| initialVisible | boolean
| false
| Whether the layer is visible on initial render or not |
| debounceTime | number
| 150
| The debounce interval for the resize
event listener |
Example
import { useLayerWithSizeConstraints } from 'react-scroll-blocking-layers'
import Modal from './Modal'
function Info() {
// the modal will only show if the viewport width is <= 800px
const [modalVisible, setModalVisible] = useLayerWithSizeConstraints(800)
return (
<>
{modalVisible && <Modal onClose={() => setModalVisible(false)} />}
<button onClick={() => setModalVisible(true)}>Open Modal</button>
</>
)
}
useLayerCount
The last hook is only useful if you want information on how many layers are active. It takes no arguments and returns a single integer.
Example
import { useLayerCount } from 'react-scroll-blocking-layers'
function Info() {
const layerCount = useLayerCount()
const isActive = layerCount > 0
return (
<p>
{layerCount} active layers.
<br />
Scroll blocking is {isActive ? 'active' : 'inactive'}
</p>
)
}
License
react-scroll-blocking-layers is licensed under the MIT License. Created with ♥ by engineers at Carla.