npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hunterliu/scroll-lock

v0.0.4

Published

A lightweight, SSR-safe scroll locking library with reference counting support. Perfect for modals, dialogs, and overlays that need to prevent background scrolling.

Readme

@hunterliu/scroll-lock

npm version npm downloads

A lightweight, SSR-safe scroll locking library with reference counting support. Perfect for modals, dialogs, and overlays that need to prevent background scrolling.

Features

  • 🔒 Reference counting - Multiple locks on the same element work properly
  • 🌐 SSR-safe - Works seamlessly in server-side rendering environments
  • 🎯 Multiple targets - Lock body, documentElement, or any HTMLElement
  • 📱 iOS support - Special touch event handling for iOS devices
  • 🔧 TypeScript - Full type safety out of the box
  • Lightweight - Minimal bundle size with zero dependencies
  • 🧹 Clean restoration - Properly restores original overflow styles

Installation

# ✨ Auto-detect (supports npm, yarn, pnpm, deno and bun)
npx nypm install @hunterliu/scroll-lock

Basic Usage

import {
  lockScroll,
  unlockScroll,
  isScrollLocked,
} from "@hunterliu/scroll-lock";

// Lock body scroll (default target)
lockScroll(document.body);

// Check if scroll is locked
console.log(isScrollLocked(document.body)); // true

// Unlock body scroll
unlockScroll(document.body);

console.log(isScrollLocked(document.body)); // false

Reference Counting

The library uses reference counting, so multiple lockScroll() calls require the same number of unlockScroll() calls:

lockScroll(document.body); // count: 1
lockScroll(document.body); // count: 2

unlockScroll(document.body); // count: 1 - still locked
unlockScroll(document.body); // count: 0 - now unlocked

Custom Targets

Lock scroll on specific elements:

const modal = document.querySelector(".modal");

// Lock specific element
lockScroll(modal);

// Check if scroll is locked
console.log(isScrollLocked(modal)); // true

// Unlock with same target
unlockScroll(modal);

Force Unlock

Bypass reference counting with force unlock:

lockScroll(document.body); // count: 1
lockScroll(document.body); // count: 2

// Force unlock ignores count
unlockScroll(document.body, { force: true }); // immediately unlocked

Clear All Locks

Reset all scroll locks across all targets:

import { clearAllScrollLocks } from "@hunterliu/scroll-lock";

// Lock multiple targets
lockScroll(document.body);
lockScroll(document.querySelector(".modal"));
lockScroll(document.querySelector(".sidebar"));

// Clear everything
clearAllScrollLocks(); // all targets unlocked

API Reference

lockScroll(target)

Lock scroll on target element.

function lockScroll(target: ScrollLockTarget): LockState | undefined;

Parameters:

  • target: ScrollLockTarget - Element to lock (defaults to document.body if null/undefined)

Returns:

  • LockState | undefined - The lock state object, or undefined if not in browser environment

unlockScroll(target, options?)

Unlock scroll on target element.

function unlockScroll(
  target: ScrollLockTarget,
  options?: {
    force?: boolean;
  },
): LockState | undefined;

Parameters:

  • target: ScrollLockTarget - Element to unlock (defaults to document.body if null/undefined)
  • options? - Optional configuration object
    • force?: boolean - Bypass reference counting (default: false)

Returns:

  • LockState | undefined - The lock state object, or undefined if not in browser environment

isScrollLocked(target)

Check if target is currently locked.

function isScrollLocked(target: ScrollLockTarget): boolean;

Parameters:

  • target: ScrollLockTarget - Element to check (defaults to document.body if null/undefined)

Returns:

  • boolean - true if the element is locked, false otherwise

clearAllScrollLocks()

Clear all scroll locks on all targets.

function clearAllScrollLocks(): void;

Types

ScrollLockTarget

type ScrollLockTarget =
  | HTMLElement
  | SVGElement
  | Window
  | Document
  | null
  | undefined;

Target Resolution:

  • HTMLElement | SVGElement - Used directly as the lock target
  • Window - Targets window.document.documentElement
  • Document - Targets document.documentElement
  • null | undefined - Defaults to document.body

LockState

interface LockState {
  count: number;
  originalOverflow?: string;
  stopTouchEventListener?: () => void;
}

Exported Constants

Advanced usage - access internal state:

import { lockStateMap, lockedElementSet } from "@hunterliu/scroll-lock";

// WeakMap storing lock state for each element
const state = lockStateMap.get(document.body);

// Set of all currently locked elements
const isLocked = lockedElementSet.has(document.body);