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

@meadown/use-is-scroll

v1.0.0

Published

A React hook to track window scroll position and document dimensions

Readme

@meadown/use-is-scroll

License: MIT React Next.js TypeScript GitHub LinkedIn

A lightweight React hook to track window scroll position and document dimensions.

AUTHOR
Dewan Mobashirul
Copyright (c) 2025 Dewan Mobashirul
All rights reserved

Table of Contents

Installation

npm install @meadown/use-is-scroll

or with yarn:

yarn add @meadown/use-is-scroll

or with pnpm:

pnpm add @meadown/use-is-scroll

Features

Core Functionality

  • Real-time Scroll Tracking - Monitor horizontal (X) and vertical (Y) scroll positions in pixels
  • Document Dimensions - Access full document scrollable width and height
  • Smart Threshold Detection - Customizable thresholds to detect when scrolling has occurred
  • Flexible Direction Control - Choose to track horizontal or vertical scrolling independently

Technical Advantages

  • Zero Dependencies - Lightweight implementation with no external dependencies
  • Full TypeScript Support - Complete type definitions included
  • Performance Optimized - Uses passive event listeners for smooth scrolling
  • Automatic Cleanup - Properly removes event listeners on component unmount
  • SSR Compatible - Works with Next.js App Router and Pages Router
  • Tree-Shakeable - ES modules support for optimal bundle sizes
  • Dual Package - Supports both ESM and CommonJS

Common Use Cases

This hook is perfect for implementing:

  • Sticky Headers/Navbars - Show/hide navigation based on scroll position
  • Scroll Progress Indicators - Display reading progress bars
  • Infinite Scroll - Load more content as user scrolls down
  • Scroll-to-Top Buttons - Show button after user scrolls past threshold
  • Parallax Effects - Create scroll-based animations
  • Analytics Tracking - Monitor user scroll depth
  • Dynamic Styling - Change UI elements based on scroll position

Usage

Basic Example (Vertical Scrolling)

import { useIsScroll } from "@meadown/use-is-scroll"

function MyComponent() {
  const { scrollY, isScrolled } = useIsScroll()

  return (
    <div>
      <p>Scroll Y: {scrollY}px</p>
      <p>Has scrolled: {isScrolled ? "Yes" : "No"}</p>
    </div>
  )
}

Horizontal Scrolling with Custom Threshold

import { useIsScroll } from "@meadown/use-is-scroll"

function MyComponent() {
  const { scrollX, isScrolled } = useIsScroll({
    direction: "x",
    xThreshold: 32,
  })

  return (
    <div>
      <p>Scroll X: {scrollX}px</p>
      <p>Has scrolled horizontally: {isScrolled ? "Yes" : "No"}</p>
    </div>
  )
}

Complete Example

import { useIsScroll } from "@meadown/use-is-scroll"

function ScrollTracker() {
  const { scrollX, scrollY, isScrolled, scrollWidth, scrollHeight } =
    useIsScroll({
      direction: "y",
      yThreshold: 100,
    })

  return (
    <div>
      <h2>Scroll Information</h2>
      <p>
        Scroll Position: X={scrollX}px, Y={scrollY}px
      </p>
      <p>
        Document Size: {scrollWidth}px × {scrollHeight}px
      </p>
      <p>Scrolled past threshold: {isScrolled ? "Yes" : "No"}</p>
    </div>
  )
}

Real-World Examples

Dynamic Threshold Based on Screen Size

import { useIsScroll } from "@meadown/use-is-scroll"
import { useState, useEffect } from "react"

function ResponsiveScrollDetection() {
  const [threshold, setThreshold] = useState(50)
  const { isScrolled } = useIsScroll({ yThreshold: threshold })

  useEffect(() => {
    // Adjust threshold based on screen width
    const updateThreshold = () => {
      setThreshold(window.innerWidth < 768 ? 30 : 50)
    }

    updateThreshold()
    window.addEventListener("resize", updateThreshold)
    return () => window.removeEventListener("resize", updateThreshold)
  }, [])

  return <div>{isScrolled ? "Scrolled!" : "At top"}</div>
}

Sticky Navigation Bar

import { useIsScroll } from "@meadown/use-is-scroll"

function StickyNav() {
  const { isScrolled } = useIsScroll({ yThreshold: 50 })

  return (
    <nav
      className={`navbar ${isScrolled ? "navbar-sticky" : ""}`}
      style={{
        position: "fixed",
        top: 0,
        width: "100%",
        background: isScrolled ? "white" : "transparent",
        boxShadow: isScrolled ? "0 2px 4px rgba(0,0,0,0.1)" : "none",
        transition: "all 0.3s ease",
      }}
    >
      <div>Your Navigation Content</div>
    </nav>
  )
}

Scroll-to-Top Button

import { useIsScroll } from "@meadown/use-is-scroll"

function ScrollToTopButton() {
  const { isScrolled, scrollY } = useIsScroll({ yThreshold: 300 })

  const scrollToTop = () => {
    window.scrollTo({ top: 0, behavior: "smooth" })
  }

  if (!isScrolled) return null

  return (
    <button
      onClick={scrollToTop}
      style={{
        position: "fixed",
        bottom: "20px",
        right: "20px",
        padding: "12px 16px",
        borderRadius: "50%",
        background: "#007bff",
        color: "white",
        border: "none",
        cursor: "pointer",
        opacity: scrollY > 300 ? 1 : 0,
        transition: "opacity 0.3s ease",
      }}
      aria-label="Scroll to top"
    >
      ↑
    </button>
  )
}

Reading Progress Bar

import { useIsScroll } from "@meadown/use-is-scroll"

function ReadingProgress() {
  const { scrollY, scrollHeight } = useIsScroll()

  const progress = Math.min(
    (scrollY / (scrollHeight - window.innerHeight)) * 100,
    100
  )

  return (
    <div
      style={{
        position: "fixed",
        top: 0,
        left: 0,
        height: "4px",
        background: "linear-gradient(to right, #4CAF50, #2196F3)",
        width: `${progress}%`,
        transition: "width 0.1s ease",
        zIndex: 9999,
      }}
    />
  )
}

API

useIsScroll(options?)

Parameters

  • options (optional): Configuration object
    • direction: 'x' | 'y' - Axis to evaluate for isScrolled (default: 'y')
    • xThreshold: number - Horizontal scroll threshold in pixels (default: 16)
    • yThreshold: number - Vertical scroll threshold in pixels (default: 16)

Returns

An object containing:

  • scrollX: number - Current horizontal scroll offset in pixels
  • scrollY: number - Current vertical scroll offset in pixels
  • isScrolled: boolean - True when the selected axis exceeds its threshold
  • scrollWidth: number - Full document scrollable width in pixels
  • scrollHeight: number - Full document scrollable height in pixels

TypeScript

This package includes TypeScript type definitions. All types are exported:

import {
  useIsScroll,
  UseIsScrollOption,
  UseIsScrollReturn,
  ScrollPosition,
  ScrollSize,
} from "@meadown/use-is-scroll"

Browser Compatibility

This hook is compatible with all modern browsers that support:

  • ES2020+ features
  • React 16.8+ (Hooks)
  • Window scroll events
  • Passive event listeners

Supported Browsers:

  • Chrome/Edge 90+
  • Firefox 88+
  • Safari 14+
  • Opera 76+

Note: This package uses modern JavaScript features and may require polyfills for older browsers.

Server-Side Rendering (SSR)

This hook is designed for client-side use and includes the "use client" directive for Next.js App Router compatibility.

Next.js App Router (13+):

"use client"
import { useIsScroll } from "@meadown/use-is-scroll"

export default function MyComponent() {
  const { scrollY, isScrolled } = useIsScroll()
  // Component code...
}

Next.js Pages Router:

import dynamic from "next/dynamic"

const ScrollComponent = dynamic(() => import("./ScrollComponent"), {
  ssr: false,
})

export default function Page() {
  return <ScrollComponent />
}

Important: The hook accesses window and document objects, which are not available during server-side rendering. Always ensure components using this hook are client-side only.

Performance Considerations

This hook is optimized for performance with the following features:

Built-in Optimizations:

  • Passive event listeners to improve scroll performance
  • Single scroll event listener per hook instance
  • Optimized with single state update per scroll event (prevents multiple re-renders)
  • Automatic cleanup on component unmount
  • Efficient dependency tracking for dynamic option changes

Best Practices:

  • Avoid using this hook in multiple components on the same page when possible
  • Consider memoizing child components that use scroll values to prevent unnecessary re-renders
  • For heavy computations based on scroll values, use useMemo or useCallback

Example with memoization:

import { useMemo } from "react"
import { useIsScroll } from "@meadown/use-is-scroll"

function MyComponent() {
  const { scrollY, isScrolled } = useIsScroll()

  const scrollPercentage = useMemo(() => {
    return Math.round((scrollY / document.documentElement.scrollHeight) * 100)
  }, [scrollY])

  return <div>Scrolled: {scrollPercentage}%</div>
}

Note: Scroll events fire frequently. If you're performing expensive operations based on scroll values, consider implementing your own throttling or debouncing logic.

Troubleshooting

Common Issues

"window is not defined" error:

  • Make sure you're using the hook only in client-side components
  • For Next.js, add "use client" directive or use dynamic imports with ssr: false

Dynamic threshold updates:

  • The hook automatically responds to changes in direction, xThreshold, and yThreshold options
  • When you change these values, the scroll listener is recreated with the new configuration
  • This allows for dynamic threshold adjustments based on your component state

High CPU usage:

  • Scroll events fire very frequently. Make sure you're not performing expensive calculations on every render
  • Use useMemo and useCallback to optimize child components
  • Consider implementing throttling for your use case

FAQ

Q: Can I use this hook multiple times in the same component? A: Yes, but each instance will add its own scroll listener. For better performance, use it once and pass the values down to child components.

Q: Does this work with custom scrollable containers? A: No, this hook specifically tracks window scroll. For custom containers, you'll need a different solution that attaches listeners to specific DOM elements.

Q: How do I track scroll percentage? A: Calculate it using the returned values:

const { scrollY, scrollHeight } = useIsScroll()
const percentage = (scrollY / (scrollHeight - window.innerHeight)) * 100

Q: Can I use this with React Native? A: No, this hook is designed for web browsers and uses web-specific APIs (window, document).

Contributing

Contributions are welcome! If you'd like to contribute:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Development Setup:

# Clone the repository
git clone https://github.com/meadown/use-is-scroll.git
cd use-is-scroll

# Install dependencies (choose one)
npm install
# or
yarn install
# or
pnpm install

# Build the package
npm run build

# Watch mode for development
npm run dev

Please ensure your code follows the existing style and includes appropriate tests.

License

MIT