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 🙏

© 2025 – Pkg Stats / Ryan Hefner

use-smart-scroll

v3.0.2

Published

Comprehensive scroll utilities for React applications

Downloads

15

Readme

use-smart-scroll

use-smart-scroll

npm downloads bundle size TypeScript License: MIT

A comprehensive collection of React hooks and utilities for smart scroll handling, animations, and interactions.

✓ Features

  • 10+ Smart Hooks - Complete scroll state management
  • Smooth Scrolling - 20+ easing functions for perfect animations
  • Scroll Spy - Track visible elements and active sections
  • Infinite Scroll - Easy infinite scrolling implementation
  • Scroll Lock - Prevent body scrolling (modals, overlays)
  • Parallax Effects - Smooth parallax scrolling
  • Scroll Animations - Trigger animations on scroll
  • Mobile Optimized - Touch-friendly and performant
  • Highly Configurable - Extensive customization options
  • TypeScript Support - Full type definitions included
  • Lightweight - Tree-shakeable, minimal bundle impact
  • Performance First - RAF, throttling, and debouncing built-in

📦 Installation

npm install use-smart-scroll
# or
bun add use-smart-scroll
# or
yarn add use-smart-scroll
# or
pnpm add use-smart-scroll

✓ Quick Start

import React from 'react';
import { useScroll, useSmoothScroll } from 'use-smart-scroll';

function App() {
  const { show, scrolled, direction, isAtTop } = useScroll();
  const { scrollToTop, scrollToElement } = useSmoothScroll();

  return (
    <div>
      {/* Smart Navigation */}
      <nav className={`navbar ${show ? 'visible' : 'hidden'} ${scrolled ? 'scrolled' : ''}`}>
        <button onClick={() => scrollToElement('#about', { duration: 800 })}>
          About
        </button>
        <button onClick={() => scrollToTop()} disabled={isAtTop}>
          Back to Top
        </button>
      </nav>
      
      {/* Discount Banner - Only at top */}
      {isAtTop && (
        <div className="discount-banner">
          🎉 Special Offer: 50% OFF! Use code: SAVE50
        </div>
      )}
      
      <main>
        <section id="hero">Hero Section</section>
        <section id="about">About Section</section>
      </main>
    </div>
  );
}

📚 Smart Hooks Overview

🎯 Core Hooks

| Hook | Purpose | Returns | |------|---------|---------| | useScroll | Complete scroll state | { show, scrolled, direction, position, velocity, isAtTop, isAtBottom } | | useSmoothScroll | Smooth scrolling utilities | { scrollTo, scrollToTop, scrollToElement, cancel } | | useScrollSpy | Track visible elements | { activeElement, visibleElements } | | useScrollProgress | Scroll progress percentage | progress (0-100) |

🎨 Advanced Hooks

| Hook | Purpose | Use Case | |------|---------|----------| | useInfiniteScroll | Infinite loading | Social feeds, product lists | | useScrollLock | Prevent scrolling | Modals, overlays | | useParallax | Parallax effects | Hero sections, backgrounds | | useScrollAnimation | Scroll-triggered animations | Fade-ins, reveals |

📖 Detailed API

useScroll(options?)

The main smart hook that provides comprehensive scroll state information.

const {
  show,           // boolean - show/hide based on scroll direction
  scrolled,       // boolean - scrolled past threshold
  scrollY,        // number - current Y position
  scrollX,        // number - current X position
  direction,      // { vertical: 'up'|'down'|'none', horizontal: 'left'|'right'|'none' }
  velocity,       // number - scroll velocity
  isScrolling,    // boolean - currently scrolling
  isAtTop,        // boolean - at top of page
  isAtBottom,     // boolean - at bottom of page
  isAtLeft,       // boolean - at left edge
  isAtRight,      // boolean - at right edge
} = useScroll({
  directionThreshold: 0,     // threshold for direction detection
  scrolledThreshold: 100,    // threshold for scrolled state
  throttle: 100,             // throttle scroll events (ms)
  debounce: 0,               // debounce scroll events (ms)
  onScroll: (state) => {},   // callback on scroll
});

Example: Smart Navigation with Discount Banner

function SmartNavigation() {
  const { show, scrolled, direction, isAtTop } = useScroll({
    scrolledThreshold: 50,
    throttle: 16, // 60fps
  });
  
  return (
    <>
      {/* Discount Banner - Only at top */}
      <div className={`
        fixed top-0 left-0 right-0 z-50 bg-red-500 text-white text-center py-2
        transition-all duration-300 transform
        ${isAtTop ? 'translate-y-0 opacity-100' : '-translate-y-full opacity-0'}
      `}>
        🎉 Special Offer: 50% OFF! Use code: SAVE50
      </div>

      {/* Smart Navigation */}
      <nav className={`
        fixed left-0 right-0 z-40 transition-all duration-300
        ${isAtTop ? 'top-10' : 'top-0'}
        ${show ? 'translate-y-0' : '-translate-y-full'}
        ${scrolled ? 'bg-white shadow-lg' : 'bg-transparent'}
      `}>
        <div className="flex justify-between items-center p-4">
          <div>Logo</div>
          <div className="text-sm">
            Direction: {direction.vertical}
          </div>
        </div>
      </nav>
    </>
  );
}

useSmoothScroll()

Provides smart smooth scrolling utilities with customizable easing functions.

const {
  scrollTo,           // (options) => Promise<void>
  scrollToTop,        // (duration?) => Promise<void>
  scrollToBottom,     // (duration?) => Promise<void>
  scrollToElement,    // (element, options?) => Promise<void>
  scrollIntoView,     // (element, options?) => Promise<void>
  cancel,             // () => void
  isScrolling,        // () => boolean
} = useSmoothScroll();

Example: Smart Navigation Menu

function SmartNavigationMenu() {
  const { scrollToElement } = useSmoothScroll();
  const { activeElement } = useScrollSpy(['#home', '#about', '#contact']);
  
  const menuItems = [
    { id: '#home', label: 'Home' },
    { id: '#about', label: 'About' },
    { id: '#contact', label: 'Contact' },
  ];
  
  return (
    <nav className="flex space-x-4">
      {menuItems.map(({ id, label }) => (
        <button
          key={id}
          onClick={() => scrollToElement(id, {
            duration: 800,
            easing: 'easeInOutCubic',
            offset: -100,
            onComplete: () => console.log('Scrolled to', label),
          })}
          className={`px-4 py-2 rounded transition-colors ${
            activeElement === id 
              ? 'bg-blue-500 text-white' 
              : 'text-gray-600 hover:text-blue-500'
          }`}
        >
          {label}
        </button>
      ))}
    </nav>
  );
}

Smart Scroll Options:

await scrollTo({
  target: 1000,                    // scroll position or element
  duration: 800,                  // animation duration
  easing: 'easeInOutCubic',       // easing function
  offset: -100,                   // offset from target
  onStart: () => console.log('Started'),
  onComplete: () => console.log('Completed'),
  onCancel: () => console.log('Cancelled'),
});

useScrollSpy(elements, options?)

Smart tracking of which elements are currently visible in the viewport.

const { activeElement, visibleElements } = useScrollSpy(
  ['#section1', '#section2', '#section3'],
  {
    offset: 100,           // offset from top
    threshold: 0.5,        // visibility threshold (0-1)
    rootMargin: '0px',     // intersection observer margin
  }
);

useScrollProgress()

Get the current scroll progress as a percentage with smart calculation.

const progress = useScrollProgress(); // 0-100

Example: Smart Reading Progress Bar

function SmartReadingProgress() {
  const progress = useScrollProgress();
  
  return (
    <div className="fixed top-0 left-0 w-full h-1 bg-gray-200 z-50">
      <div 
        className="h-full bg-gradient-to-r from-blue-500 to-purple-600 transition-all duration-150"
        style={{ width: `${progress}%` }}
      />
    </div>
  );
}

useInfiniteScroll(options)

Smart infinite scrolling with loading states and error handling.

const { setTarget, isLoading } = useInfiniteScroll({
  hasMore: true,                    // has more items to load
  onLoadMore: async () => {         // load more function
    await fetchMoreItems();
  },
  threshold: 100,                   // distance from bottom to trigger
  rootMargin: '100px',             // trigger area
});

Example: Smart Product List

function SmartProductList() {
  const [products, setProducts] = useState([]);
  const [hasMore, setHasMore] = useState(true);
  const [loading, setLoading] = useState(false);
  
  const { setTarget } = useInfiniteScroll({
    hasMore,
    onLoadMore: async () => {
      setLoading(true);
      try {
        const newProducts = await fetchProducts();
        setProducts(prev => [...prev, ...newProducts]);
        if (newProducts.length === 0) setHasMore(false);
      } catch (error) {
        console.error('Failed to load products:', error);
      } finally {
        setLoading(false);
      }
    },
  });
  
  return (
    <div className="space-y-4">
      {products.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
      
      {hasMore && (
        <div ref={setTarget} className="p-8 text-center">
          {loading ? (
            <>
              <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500 mx-auto"></div>
              <p className="mt-2 text-gray-600">Loading more products...</p>
            </>
          ) : (
            <p className="text-gray-500">Scroll to load more</p>
          )}
        </div>
      )}
      
      {!hasMore && (
        <div className="text-center p-8">
          <p className="text-gray-600">🎉 You've seen all products!</p>
        </div>
      )}
    </div>
  );
}

useScrollLock(options?)

Smart scroll locking for modals and overlays with mobile support.

const { lock, unlock, toggle, isLocked } = useScrollLock({
  reserveScrollBarGap: true,        // maintain scrollbar space
  allowTouchMove: (el) => false,    // allow touch move on specific elements
});

Example: Smart Modal with Scroll Lock

function SmartModal({ isOpen, onClose, children }) {
  const { lock, unlock, isLocked } = useScrollLock();
  
  useEffect(() => {
    if (isOpen) {
      lock();
    } else {
      unlock();
    }
    
    return () => unlock(); // cleanup
  }, [isOpen, lock, unlock]);
  
  if (!isOpen) return null;
  
  return (
    <div className="fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50 p-4">
      <div className="bg-white rounded-2xl p-6 max-w-md w-full transform animate-fade-in-up">
        <div className="flex justify-between items-center mb-4">
          <h3 className="text-xl font-bold">Modal Title</h3>
          <button 
            onClick={onClose}
            className="text-gray-500 hover:text-gray-700 text-2xl"
          >
            ×
          </button>
        </div>
        {children}
        <div className="mt-4 text-sm text-gray-500">
          Scroll Lock: {isLocked ? 'Active' : 'Inactive'}
        </div>
      </div>
    </div>
  );
}

useParallax(options?)

Create smart parallax scrolling effects with performance optimization.

const parallaxRef = useParallax({
  speed: 0.5,          // parallax speed (0-1)
  offset: 0,           // initial offset
  min: -100,           // minimum transform value
  max: 100,            // maximum transform value
});

Example: Smart Hero Section

function SmartHeroSection() {
  const parallaxRef = useParallax({ speed: 0.5 });
  const { scrollY } = useScroll();
  
  return (
    <section className="relative h-screen overflow-hidden">
      <div 
        ref={parallaxRef}
        className="absolute inset-0 bg-cover bg-center"
        style={{
          backgroundImage: 'url("/hero-bg.jpg")',
        }}
      />
      <div className="relative z-10 flex items-center justify-center h-full bg-black/30">
        <div className="text-center text-white">
          <h1 className="text-6xl font-bold mb-4">Smart Scrolling</h1>
          <p className="text-xl mb-8">Experience the power of use-smart-scroll</p>
          <div className="text-sm opacity-75">
            Scroll Position: {scrollY}px
          </div>
        </div>
      </div>
    </section>
  );
}

useScrollAnimation(options?)

Smart scroll-triggered animations with intersection observer.

const { elementRef, reset } = useScrollAnimation({
  threshold: 0.1,                   // intersection threshold
  triggerOnce: true,                // trigger only once
  animationClass: 'fade-in',        // CSS class to add
  animationDelay: 200,              // delay before animation
});

Example: Smart Animated Cards

function SmartAnimatedCard({ children, delay = 0 }) {
  const { elementRef } = useScrollAnimation({
    animationClass: 'animate-fade-in-up',
    threshold: 0.2,
    animationDelay: delay,
  });
  
  return (
    <div ref={elementRef} className="opacity-0 transform translate-y-8">
      {children}
    </div>
  );
}

function SmartFeatureSection() {
  return (
    <section className="py-20 bg-gray-50">
      <div className="max-w-6xl mx-auto px-4">
        <h2 className="text-4xl font-bold text-center mb-16">Smart Features</h2>
        <div className="grid md:grid-cols-3 gap-8">
          <SmartAnimatedCard delay={0}>
            <div className="bg-white p-6 rounded-xl shadow-lg">
              <h3 className="text-xl font-bold mb-4">🚀 Performance</h3>
              <p>Optimized with RAF and throttling</p>
            </div>
          </SmartAnimatedCard>
          
          <SmartAnimatedCard delay={200}>
            <div className="bg-white p-6 rounded-xl shadow-lg">
              <h3 className="text-xl font-bold mb-4">📱 Mobile First</h3>
              <p>Touch-friendly and responsive</p>
            </div>
          </SmartAnimatedCard>
          
          <SmartAnimatedCard delay={400}>
            <div className="bg-white p-6 rounded-xl shadow-lg">
              <h3 className="text-xl font-bold mb-4">🎯 TypeScript</h3>
              <p>Full type safety included</p>
            </div>
          </SmartAnimatedCard>
        </div>
      </div>
    </section>
  );
}

// CSS
.animate-fade-in-up {
  animation: fadeInUp 0.6s ease-out forwards;
}

@keyframes fadeInUp {
  from {
    opacity: 0;
    transform: translateY(30px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

🎨 Smart Easing Functions

Available easing functions for smooth scrolling:

Basic

  • linear
  • easeInQuad, easeOutQuad, easeInOutQuad

Cubic

  • easeInCubic, easeOutCubic, easeInOutCubic

Advanced

  • easeInQuart, easeOutQuart, easeInOutQuart
  • easeInQuint, easeOutQuint, easeInOutQuint
  • easeInSine, easeOutSine, easeInOutSine
  • easeInExpo, easeOutExpo, easeInOutExpo
  • easeInCirc, easeOutCirc, easeInOutCirc
  • easeInBack, easeOutBack, easeInOutBack
  • easeInElastic, easeOutElastic, easeInOutElastic
  • easeInBounce, easeOutBounce, easeInOutBounce

🛠️ Utility Functions

import {
  // Scroll Control
  scrollTo,
  scrollToTop,
  scrollToBottom,
  scrollToElement,
  scrollIntoView,
  cancelScroll,
  
  // Position & Measurement
  getScrollPosition,
  getDocumentDimensions,
  getViewportDimensions,
  getElementOffset,
  getScrollProgress,
  
  // Detection & Validation
  isElementInViewport,
  isScrollable,
  getScrollableParent,
  
  // Performance
  throttle,
  debounce,
  rafThrottle,
  calculateVelocity,
  preventScroll,
  getScrollbarWidth,
} from 'use-smart-scroll';

🎯 Real-world Examples

E-commerce Website

import { useScroll, useInfiniteScroll, useScrollSpy, useSmoothScroll } from 'use-smart-scroll';

function EcommerceApp() {
  const { show, scrolled, isAtTop } = useScroll();
  const { scrollToTop } = useSmoothScroll();
  
  return (
    <div>
      {/* Discount Banner */}
      {isAtTop && (
        <div className="bg-red-500 text-white text-center py-2">
          🔥 Flash Sale: 70% OFF Everything!
        </div>
      )}
      
      {/* Smart Navigation */}
      <nav className={`sticky top-0 z-40 transition-all ${
        show ? 'translate-y-0' : '-translate-y-full'
      } ${scrolled ? 'bg-white shadow-lg' : 'bg-transparent'}`}>
        <div className="flex justify-between items-center p-4">
          <div>🛍️ Smart Store</div>
          <div className="flex space-x-4">
            <a href="#products">Products</a>
            <a href="#about">About</a>
            <button onClick={() => scrollToTop()}>🔝</button>
          </div>
        </div>
      </nav>
      
      {/* Product Grid with Infinite Scroll */}
      <ProductGrid />
    </div>
  );
}

Blog/News Website

function BlogApp() {
  const progress = useScrollProgress();
  const { activeElement } = useScrollSpy(['#intro', '#content', '#conclusion']);
  
  return (
    <div>
      {/* Reading Progress */}
      <div className="fixed top-0 w-full h-1 bg-gray-200 z-50">
        <div 
          className="h-full bg-blue-500 transition-all"
          style={{ width: `${progress}%` }}
        />
      </div>
      
      {/* Table of Contents */}
      <nav className="fixed left-4 top-1/2 transform -translate-y-1/2">
        {['intro', 'content', 'conclusion'].map(section => (
          <div key={section} className={`w-2 h-8 mb-2 rounded ${
            activeElement === `#${section}` ? 'bg-blue-500' : 'bg-gray-300'
          }`} />
        ))}
      </nav>
      
      <article>
        <section id="intro">Introduction</section>
        <section id="content">Main Content</section>
        <section id="conclusion">Conclusion</section>
      </article>
    </div>
  );
}

Landing Page

function LandingPage() {
  const { show, direction } = useScroll();
  const { scrollToElement } = useSmoothScroll();
  const parallaxRef = useParallax({ speed: 0.3 });
  
  return (
    <div>
      {/* Hero with Parallax */}
      <section className="relative h-screen overflow-hidden">
        <div ref={parallaxRef} className="absolute inset-0 bg-gradient-to-r from-blue-500 to-purple-600" />
        <div className="relative z-10 flex items-center justify-center h-full text-white">
          <div className="text-center">
            <h1 className="text-6xl font-bold mb-8">use-smart-scroll</h1>
            <p className="text-xl mb-8">Smart scrolling for modern React apps</p>
            <button 
              onClick={() => scrollToElement('#features', { duration: 1000 })}
              className="bg-white text-blue-500 px-8 py-3 rounded-full font-semibold hover:bg-gray-100"
            >
              Explore Features
            </button>
          </div>
        </div>
      </section>
      
      {/* Smart Navigation */}
      <nav className={`fixed top-0 w-full z-40 transition-all duration-300 ${
        show ? 'translate-y-0' : '-translate-y-full'
      } bg-white/90 backdrop-blur-md shadow-lg`}>
        <div className="flex justify-center space-x-8 p-4">
          <button onClick={() => scrollToElement('#hero')}>Home</button>
          <button onClick={() => scrollToElement('#features')}>Features</button>
          <button onClick={() => scrollToElement('#examples')}>Examples</button>
          <button onClick={() => scrollToElement('#contact')}>Contact</button>
        </div>
      </nav>
      
      {/* Animated Features */}
      <SmartFeatureSection />
    </div>
  );
}

📱 Mobile Optimization

// Mobile-specific configurations
const { show, scrolled } = useScroll({
  throttle: 16,              // 60fps for smooth mobile
  directionThreshold: 10,    // Larger threshold for touch
});

// Touch-friendly scroll lock
const { lock, unlock } = useScrollLock({
  allowTouchMove: (el) => {
    // Allow scrolling inside specific elements
    return el.closest('.scrollable-content') !== null;
  },
});

// Mobile-optimized infinite scroll
const { setTarget } = useInfiniteScroll({
  threshold: 200,            // Larger threshold for mobile
  rootMargin: '200px',       // Preload earlier on mobile
});

⚡ Performance Tips

1. Optimize Throttling

// For 60fps smooth performance
const { show } = useScroll({ throttle: 16 });

// For battery saving
const { show } = useScroll({ throttle: 100 });

2. Use Debouncing for Heavy Operations

const { scrollY } = useScroll({
  debounce: 300,
  onScroll: (state) => {
    // Heavy operations here
    updateAnalytics(state);
  },
});

3. Optimize Scroll Spy

// Use larger thresholds for better performance
const { activeElement } = useScrollSpy(elements, {
  threshold: 0.5,
  rootMargin: '100px',
});

🔧 Configuration

Global Configuration

// Create a custom hook with your defaults
function useAppScroll(options = {}) {
  return useScroll({
    throttle: 16,
    scrolledThreshold: 100,
    ...options,
  });
}

TypeScript Configuration

import type { 
  ScrollState, 
  ScrollToOptions, 
  UseScrollOptions 
} from 'use-smart-scroll';

// Custom scroll handler
const handleScroll = (state: ScrollState) => {
  console.log('Scroll state:', state);
};

// Custom scroll options
const scrollOptions: ScrollToOptions = {
  duration: 800,
  easing: 'easeInOutCubic',
  offset: -100,
};

🧪 Testing

import { renderHook, act } from '@testing-library/react';
import { useScroll } from 'use-smart-scroll';

test('should detect scroll direction', () => {
  const { result } = renderHook(() => useScroll());
  
  act(() => {
    // Simulate scroll
    Object.defineProperty(window, 'scrollY', { value: 100 });
    window.dispatchEvent(new Event('scroll'));
  });
  
  expect(result.current.direction.vertical).toBe('down');
});

📄 License

MIT © Eyachir Arafat

🙏 Acknowledgments

  • Inspired by modern scroll libraries
  • Built with performance and accessibility in mind
  • Community feedback

📞 Support


Made with ❤️ for the React community

⭐ Star on GitHub📦 View on NPM