React component that disables scroll outside of children node while maintaining scroll functionality within.
npx @tessl/cli install tessl/npm-react-remove-scroll@2.7.00
# React Remove Scroll
1
2
React Remove Scroll is a TypeScript React library that prevents scrolling outside of specified child elements while maintaining scroll functionality within those elements. It's designed for modals, dropdowns, and overlay components where background scrolling should be disabled.
3
4
## Package Information
5
6
- **Package Name**: react-remove-scroll
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install react-remove-scroll`
10
11
## Core Imports
12
13
```typescript
14
import { RemoveScroll } from "react-remove-scroll";
15
```
16
17
CommonJS:
18
```javascript
19
const { RemoveScroll } = require("react-remove-scroll");
20
```
21
22
Alternative imports for bundle splitting:
23
```typescript
24
// UI-only component (400 bytes)
25
import { RemoveScroll } from "react-remove-scroll/UI";
26
import sidecar from "react-remove-scroll/sidecar";
27
28
<RemoveScroll sideCar={sidecar}>Content</RemoveScroll>
29
```
30
31
## Basic Usage
32
33
```typescript
34
import { RemoveScroll } from "react-remove-scroll";
35
36
function Modal({ isOpen, children }) {
37
return (
38
<RemoveScroll enabled={isOpen}>
39
{children}
40
</RemoveScroll>
41
);
42
}
43
44
// Basic scroll prevention
45
<RemoveScroll>
46
<div>Only this content is scrollable</div>
47
</RemoveScroll>
48
```
49
50
## Architecture
51
52
React Remove Scroll uses a layered architecture:
53
54
- **Main Component**: `RemoveScroll` - combines UI and side effects through a sidecar pattern
55
- **UI Component**: Lightweight component that handles rendering and event setup
56
- **Sidecar Effects**: Separate module containing scroll prevention logic, event listeners, and DOM manipulations
57
- **Type System**: Comprehensive TypeScript interfaces supporting both container and forward-props patterns
58
59
## Capabilities
60
61
### Core Component
62
63
Main scroll prevention component with comprehensive configuration options.
64
65
```typescript { .api }
66
/**
67
* React component that prevents scrolling outside of its children while maintaining
68
* scroll functionality within the children. Supports both container and forward-props modes.
69
*/
70
const RemoveScroll: React.ForwardRefExoticComponent<
71
IRemoveScrollProps & React.RefAttributes<HTMLElement>
72
> & {
73
classNames: {
74
fullWidth: string;
75
zeroRight: string;
76
};
77
};
78
79
type IRemoveScrollProps = IRemoveScrollSelfProps & (ChildrenNode | ChildrenForward);
80
81
interface IRemoveScrollSelfProps {
82
/** Enable/disable scroll lock behavior */
83
enabled?: boolean; // default: true
84
/** Control removal of document scrollbar */
85
removeScrollBar?: boolean; // default: true
86
/** Allow pinch-to-zoom gestures (may break scroll isolation) */
87
allowPinchZoom?: boolean; // default: false
88
/** Prevent setting position:relative on body */
89
noRelative?: boolean; // default: false
90
/** Disable event isolation outside the lock */
91
noIsolation?: boolean; // default: false
92
/** Use pointer-events:none for complete page isolation */
93
inert?: boolean; // default: false
94
/** Additional elements to include in the scroll lock - array of refs or DOM elements that should remain interactive */
95
shards?: Array<React.RefObject<any> | HTMLElement>;
96
/** Container element type when not using forwardProps */
97
as?: string | React.ElementType; // default: 'div'
98
/** Strategy for filling scrollbar gap - 'margin' adds margin, 'padding' adds padding */
99
gapMode?: 'padding' | 'margin'; // default: 'margin'
100
/** CSS class for container */
101
className?: string;
102
/** Inline styles for container */
103
style?: React.CSSProperties;
104
/** Forwarded ref to the container element */
105
ref?: React.Ref<HTMLElement>;
106
}
107
108
interface ChildrenNode {
109
/** Wrap children in a container element */
110
forwardProps?: false;
111
children: React.ReactNode;
112
}
113
114
interface ChildrenForward {
115
/** Forward props directly to the single child element */
116
forwardProps: true;
117
children: React.ReactElement;
118
}
119
```
120
121
**Usage Examples:**
122
123
```typescript
124
import { RemoveScroll } from "react-remove-scroll";
125
126
// Container mode (default)
127
<RemoveScroll className="modal-container">
128
<div>Scrollable content</div>
129
</RemoveScroll>
130
131
// Forward props mode
132
<RemoveScroll forwardProps>
133
<div className="custom-container">
134
Scrollable content
135
</div>
136
</RemoveScroll>
137
138
// Advanced configuration
139
<RemoveScroll
140
enabled={isModalOpen}
141
allowPinchZoom={true}
142
removeScrollBar={true}
143
shards={[buttonRef, headerRef]}
144
gapMode="padding"
145
>
146
<div>Modal content</div>
147
</RemoveScroll>
148
```
149
150
### Static Properties
151
152
CSS class names for handling position:fixed elements when scroll lock is active.
153
154
```typescript { .api }
155
RemoveScroll.classNames: {
156
/** Class for full-width fixed elements */
157
fullWidth: string;
158
/** Class for right-aligned fixed elements */
159
zeroRight: string;
160
};
161
```
162
163
**Usage Examples:**
164
165
```typescript
166
import { RemoveScroll } from "react-remove-scroll";
167
import cx from "classnames";
168
169
// Full-width fixed header
170
<header className={cx("fixed-header", RemoveScroll.classNames.fullWidth)}>
171
Header content
172
</header>
173
174
// Right-aligned fixed sidebar
175
<aside className={cx("fixed-sidebar", RemoveScroll.classNames.zeroRight)}>
176
Sidebar content
177
</aside>
178
```
179
180
### Bundle Splitting Components
181
182
Separate UI and sidecar components for optimized bundle loading.
183
184
```typescript { .api }
185
/**
186
* UI-only component requiring a sidecar for side effects
187
* Import from: react-remove-scroll/UI
188
*/
189
const RemoveScroll: React.ForwardRefExoticComponent<
190
IRemoveScrollUIProps & React.RefAttributes<HTMLElement>
191
>;
192
193
interface IRemoveScrollUIProps extends IRemoveScrollProps {
194
/** Side effect component for scroll prevention logic */
195
sideCar: React.FC<any>;
196
}
197
198
/**
199
* Sidecar component containing scroll prevention side effects
200
* Import from: react-remove-scroll/sidecar
201
*/
202
const sidecar: React.FC;
203
```
204
205
**Usage Examples:**
206
207
```typescript
208
import { RemoveScroll } from "react-remove-scroll/UI";
209
import sidecar from "react-remove-scroll/sidecar";
210
211
// Manual sidecar usage
212
<RemoveScroll sideCar={sidecar}>
213
<div>Content with side effects</div>
214
</RemoveScroll>
215
216
// Dynamic sidecar loading using use-sidecar
217
import { sidecar } from "use-sidecar";
218
219
const dynamicSidecar = sidecar(() => import('react-remove-scroll/sidecar'));
220
221
<RemoveScroll sideCar={dynamicSidecar}>
222
<div>Content with dynamically loaded side effects</div>
223
</RemoveScroll>
224
```
225
226
### Advanced Configuration
227
228
#### Shards System
229
230
Shards allow additional DOM elements to remain interactive while scroll is locked:
231
232
```typescript
233
import { useRef } from 'react';
234
import { RemoveScroll } from 'react-remove-scroll';
235
236
function ModalWithShards() {
237
const buttonRef = useRef<HTMLButtonElement>(null);
238
const headerRef = useRef<HTMLElement>(null);
239
240
return (
241
<>
242
{/* These elements remain interactive due to shards */}
243
<button ref={buttonRef}>Close Modal</button>
244
<header ref={headerRef}>App Header</header>
245
246
<RemoveScroll shards={[buttonRef, headerRef]} enabled={isModalOpen}>
247
<div>Modal content - scroll is locked everywhere else</div>
248
</RemoveScroll>
249
</>
250
);
251
}
252
```
253
254
#### Gap Mode Strategies
255
256
```typescript
257
// margin mode (default) - adds margin-right to body to compensate for removed scrollbar
258
<RemoveScroll gapMode="margin">
259
<div>Content</div>
260
</RemoveScroll>
261
262
// padding mode - adds padding-right to body to compensate for removed scrollbar
263
<RemoveScroll gapMode="padding">
264
<div>Content</div>
265
</RemoveScroll>
266
```
267
268
#### Performance Optimization
269
270
```typescript
271
// For better performance on large scrollable areas - disables event isolation
272
<RemoveScroll noIsolation>
273
<div>Large scrollable content</div>
274
</RemoveScroll>
275
276
// Complete isolation using pointer-events (use carefully - not portal-friendly)
277
<RemoveScroll inert>
278
<div>Modal content</div>
279
</RemoveScroll>
280
```
281
282
### Type Definitions
283
284
```typescript { .api }
285
/** Scroll axis direction */
286
type Axis = 'v' | 'h';
287
288
/** Strategy for filling scrollbar gap */
289
type GapMode = 'padding' | 'margin';
290
291
/** Event handling callbacks for scroll prevention - internal use by sidecar */
292
interface RemoveScrollEffectCallbacks {
293
/** Handles scroll events on the locked element */
294
onScrollCapture(event: Event): void;
295
/** Handles wheel events for mouse scroll prevention */
296
onWheelCapture(event: WheelEvent): void;
297
/** Handles touch move events for touch scroll prevention */
298
onTouchMoveCapture(event: TouchEvent): void;
299
}
300
301
/** Internal props interface for side effect component */
302
interface IRemoveScrollEffectProps {
303
/** Prevent setting position:relative on body */
304
noRelative?: boolean;
305
/** Disable event isolation outside the lock */
306
noIsolation?: boolean;
307
/** Control removal of document scrollbar */
308
removeScrollBar?: boolean;
309
/** Allow pinch-to-zoom gestures */
310
allowPinchZoom: boolean;
311
/** Use pointer-events:none for complete page isolation */
312
inert?: boolean;
313
/** Additional elements to include in the scroll lock */
314
shards?: Array<React.RefObject<any> | HTMLElement>;
315
/** Reference to the lock container element */
316
lockRef: React.RefObject<HTMLElement>;
317
/** Strategy for filling scrollbar gap */
318
gapMode?: GapMode;
319
/** Callback to set event handlers */
320
setCallbacks(cb: RemoveScrollEffectCallbacks): void;
321
}
322
323
/** Component type with static properties */
324
type RemoveScrollType = React.ForwardRefExoticComponent<
325
IRemoveScrollProps & React.RefAttributes<HTMLElement>
326
> & {
327
classNames: {
328
fullWidth: string;
329
zeroRight: string;
330
};
331
};
332
```
333
334
## Error Handling
335
336
React Remove Scroll handles common edge cases automatically:
337
338
- **Mobile Safari**: Prevents zoom on touch events while maintaining scroll
339
- **Shadow DOM**: Traverses shadow boundaries for proper event handling
340
- **Portals**: Compatible with React portals through event system integration
341
- **Multiple Locks**: Supports nested scroll locks with proper cleanup
342
- **RTL Support**: Handles right-to-left text direction for horizontal scrolling
343
344
Common issues:
345
346
- **Performance**: Uses non-passive event listeners which may impact scroll performance on large scrollable areas. Consider `noIsolation` mode for better performance.
347
- **Inert Mode**: The `inert` prop is not React portal-friendly and may cause issues in production.
348
- **Pinch Zoom**: Enabling `allowPinchZoom` may break scroll isolation in some scenarios.
349
350
## Browser Compatibility
351
352
- **React Versions**: 16.8.0+ (requires hooks)
353
- **Node Versions**: 10+
354
- **TypeScript**: Full type definitions included
355
- **Peer Dependencies**: React 16.8+ || 17.x || 18.x || 19.x