or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.md

index.mddocs/

0

# 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