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

@rethink-js/rt-smooth-scroll

v1.4.0

Published

Lightweight smooth scrolling powered by Lenis with automatic loading, attribute config, and multi-instance support.

Readme

rt-smooth-scroll

Platform: Web JavaScript npm version jsDelivr hits License: MIT

rt-smooth-scroll is a lightweight JavaScript library that seamlessly integrates the Lenis smooth scroll engine into your sites with:

  • Automatic Lenis loading (no extra installs needed)
  • Zero-config defaults (Lenis defaults, unless you override via attributes)
  • Support for multiple smooth scroll instances
  • A clean global API under window.rtSmoothScroll
  • Smart Scroll-To actions with indexed selectors and dynamic offsets
  • Automatic Anchor Link Conversion (hijack native links for smooth scrolling)
  • Automatic DOM Resize Detection (uses ResizeObserver to update scroll height on dynamic content changes)
  • Scroll-To Completion Hooks (run actions/functions after a scroll-to completes)
  • Per-instance configuration via HTML attributes
  • Debug Mode with helpful console logs for development

Lenis (GitHub): https://github.com/darkroomengineering/lenis


Table of Contents


1. Installation

1.1 CDN (jsDelivr)

<script src="[https://cdn.jsdelivr.net/npm/@rethink-js/rt-smooth-scroll@latest/dist/index.min.js](https://cdn.jsdelivr.net/npm/@rethink-js/rt-smooth-scroll@latest/dist/index.min.js)"></script>

1.2 npm

npm install @rethink-js/rt-smooth-scroll

Then bundle or load dist/index.min.js as appropriate for your build setup.


2. Quick Start

Add the script to your page. With no configuration provided, rt-smooth-scroll will:

  • Activate itself automatically (if you didn’t explicitly opt out)
  • Load Lenis from CDN
  • Create a root smooth scroll instance
  • Expose the global API

Example:

<script src="[https://cdn.jsdelivr.net/npm/@rethink-js/rt-smooth-scroll@latest/dist/index.min.js](https://cdn.jsdelivr.net/npm/@rethink-js/rt-smooth-scroll@latest/dist/index.min.js)"></script>

Note: If you do not set any rt-smooth-scroll-* config attributes, the root instance uses Lenis defaults.


3. Activation Rules

The library is activated when:

  • The attribute rt-smooth-scroll exists on <html> or <body> OR
  • You place one or more elements with rt-smooth-scroll-instance

If neither is present and no instance elements are found, it auto-enables itself on <body> by adding rt-smooth-scroll (so you get a working root instance by default).


4. Configuration (HTML Attributes)

Root Mode

Add to <html> or <body> to enable:

<body rt-smooth-scroll></body>

Global Options

Place on <html> or <body> to configure defaults:

<body
  rt-smooth-scroll
  rt-smooth-scroll-lerp="0.2"
  rt-smooth-scroll-wheel-multiplier="1"
  rt-smooth-scroll-easing="easeOutCubic"
  rt-smooth-scroll-debug="true"
></body>

Important Lenis behavior:

  • duration and easing are useless if lerp is defined (this is how Lenis works).

Core attributes:

| Attribute | Description | | ----------------------------------------- | ------------------------------------------------------------ | | rt-smooth-scroll-duration | Lenis duration (only applies when lerp is not used) | | rt-smooth-scroll-lerp | Lenis lerp (0 → 1) | | rt-smooth-scroll-orientation | Lenis orientation | | rt-smooth-scroll-gesture-orientation | Lenis gestureOrientation | | rt-smooth-scroll-normalize-wheel | Alias of Lenis smoothWheel (legacy naming supported) | | rt-smooth-scroll-smooth-wheel | Lenis smoothWheel | | rt-smooth-scroll-wheel-multiplier | Lenis wheelMultiplier | | rt-smooth-scroll-touch-multiplier | Lenis touchMultiplier | | rt-smooth-scroll-sync-touch | Lenis syncTouch | | rt-smooth-scroll-sync-touch-lerp | Lenis syncTouchLerp | | rt-smooth-scroll-touch-inertia-exponent | Lenis touchInertiaExponent | | rt-smooth-scroll-infinite | Lenis infinite | | rt-smooth-scroll-auto-resize | Lenis autoResize (window resize only) | | rt-smooth-scroll-overscroll | Lenis overscroll | | rt-smooth-scroll-anchors | Lenis anchors (boolean or JSON) | | rt-smooth-scroll-auto-toggle | Lenis autoToggle | | rt-smooth-scroll-allow-nested-scroll | Lenis allowNestedScroll | | rt-smooth-scroll-easing | Named easing function (only applies when lerp is not used) | | rt-smooth-scroll-options-json | Merge additional Lenis options via JSON | | rt-smooth-scroll-debug | Enable/disable console logging (default: true) | | rt-smooth-scroll-resize-debounce-ms | Delay (ms) for resize calculations (default: 0) |

Easing options included:

  • linear
  • easeInQuad
  • easeOutQuad
  • easeInOutQuad
  • easeInCubic
  • easeOutCubic
  • easeInOutCubic
  • easeInOutSine
  • easeOutExpo

Per-Instance Configuration

Add attributes to any scroll container:

<div
  rt-smooth-scroll-instance
  rt-smooth-scroll-id="panel"
  rt-smooth-scroll-content=".scroll-content"
  rt-smooth-scroll-lerp="0.18"
></div>

| Attribute | Description | | --------------------------- | -------------------------------------------------------------- | | rt-smooth-scroll-instance | Marks scroll container | | rt-smooth-scroll-id | Optional instance identifier | | rt-smooth-scroll-content | Selector inside container (defaults to first child if omitted) |

Advanced Selectors (wrapper/content/eventsTarget)

You can map Lenis DOM targets using selectors:

<body
  rt-smooth-scroll
  rt-smooth-scroll-wrapper="#page-wrapper"
  rt-smooth-scroll-content="#page-content"
  rt-smooth-scroll-events-target="#page-wrapper"
></body>

Or per instance:

<div
  rt-smooth-scroll-instance
  rt-smooth-scroll-id="panel"
  rt-smooth-scroll-wrapper="#panel-wrapper"
  rt-smooth-scroll-content=".panel-content"
  rt-smooth-scroll-events-target="#panel-wrapper"
></div>

5. Scroll-To Actions

You can trigger a smooth scroll to any element, position, or specific instance using the rt-smooth-scroll-to attribute on any clickable element.

Basic Usage

<button rt-smooth-scroll-to="#footer">Go to Footer</button>

<button rt-smooth-scroll-to="top">Back to Top</button>

<button rt-smooth-scroll-to="500">Go to 500px</button>

Indexed Selectors

You can target elements by class order (1-based index) using .class(N) syntax.

<button rt-smooth-scroll-to=".section(2)">Go to Section 2</button>

Customization Attributes

You can customize the scroll behavior for specific triggers.

| Attribute | Description | | ---------------------------- | ------------------------------------------------------------------ | | rt-smooth-scroll-offset | Offset in pixels. Supports selectors! (See below) | | rt-smooth-scroll-duration | Override scroll duration for this action | | rt-smooth-scroll-immediate | Jump instantly (true/false) | | rt-smooth-scroll-lock | Lock scroll during animation | | rt-smooth-scroll-force | Force scroll even if stopped | | rt-smooth-scroll-target-id | Explicitly target a specific scroll instance ID (e.g. "panel") |

Dynamic Element Offsets

Instead of hardcoding pixels, you can pass a selector to rt-smooth-scroll-offset. The library will calculate that element's offsetHeight and apply it as a negative offset (perfect for sticky headers).

<button rt-smooth-scroll-to="#about" rt-smooth-scroll-offset="#nav">
  About
</button>

Scroll-To Completion Hooks

When a scroll-to finishes (including the built-in correction pass for layout shifts), you can run an action/function.

Supported hook attributes:

  • Per-trigger: rt-smooth-scroll-on-complete
  • Global default: rt-smooth-scroll-on-complete on <html> or <body>

5.1 Per-trigger completion action

Click an element after the scroll completes:

<a rt-smooth-scroll-to="#contact" rt-smooth-scroll-on-complete="#nav-show">
  Contact
</a>

The shorthand above treats the value as a selector and will click() it.

You can also be explicit:

<a
  rt-smooth-scroll-to="#contact"
  rt-smooth-scroll-on-complete="click:#nav-show"
>
  Contact
</a>

5.2 Global default completion action

Apply to all scroll-to triggers unless they override it:

<body rt-smooth-scroll rt-smooth-scroll-on-complete="click:#nav-show"></body>

5.3 Supported completion action formats

1) Selector-only (defaults to click)

<button rt-smooth-scroll-to="#x" rt-smooth-scroll-on-complete="#nav-show">
  Go
</button>

2) Typed strings

  • click:#selector
  • focus:#selector
  • dispatch:event-name (fires CustomEvent on window)
  • call:functionName (calls window[functionName])

Examples:

<button rt-smooth-scroll-to="#x" rt-smooth-scroll-on-complete="focus:#search">
  Go
</button>

<button
  rt-smooth-scroll-to="#x"
  rt-smooth-scroll-on-complete="dispatch:rt:smooth-scroll:complete"
>
  Go
</button>

<button
  rt-smooth-scroll-to="#x"
  rt-smooth-scroll-on-complete="call:afterScroll"
>
  Go
</button>

When using call:functionName, your function receives a single object:

window.afterScroll = function (ctx) {
  console.log("afterScroll", ctx);
};

ctx includes:

  • lenis (the Lenis instance used)
  • trigger (the element that initiated the scroll)
  • target (resolved target number/element/window)
  • value (the raw rt-smooth-scroll-to string)
  • id (the explicit rt-smooth-scroll-target-id if provided)

3) JSON actions (single or array)

This is the most robust option for multi-step actions:

<button
  rt-smooth-scroll-to="#x"
  rt-smooth-scroll-on-complete='[
    {"type":"click","selector":"#nav-show"},
    {"type":"dispatch","name":"rt:smooth-scroll:complete","detail":{"from":"scroll-to"}}
  ]'
>
  Go
</button>

Supported JSON action types:

  • { "type": "click", "selector": "#el" }
  • { "type": "focus", "selector": "#el" }
  • { "type": "dispatch", "name": "event-name", "detail": any }
  • { "type": "call", "name": "functionName", "detail": any }

For dispatch and call, detail is merged into the context under detail.


6. Anchor Link Conversion

You can automatically convert standard <a> tags (e.g., <a href="#contact">) into smooth scroll triggers without manually adding attributes to every link.

Enable Conversion

Add this attribute to your <body> or <html> tag:

<body rt-smooth-scroll rt-smooth-scroll-anchor-links="true"></body>

How it works

  1. Auto-Detection: Finds all links pointing to a hash on the current page.
  2. Hijacking: Converts them to use the rt-smooth-scroll-to logic.
  3. Clean URLs: Removes the href attribute so the browser URL bar does not update (no #hash in URL), keeping your history clean.
  4. Accessibility: Automatically restores tabindex="0", role="button", cursor: pointer, and keyboard Enter / Space key support.

Anchor link completion hook (auto-injected)

You can automatically attach a completion hook to every converted anchor link:

<body
  rt-smooth-scroll
  rt-smooth-scroll-anchor-links="true"
  rt-smooth-scroll-anchor-links-on-complete="click:#nav-show"
></body>

Rules:

  • If a link already has rt-smooth-scroll-on-complete, it is not overridden.
  • If rt-smooth-scroll-anchor-links-on-complete exists, it is copied into each converted link as rt-smooth-scroll-on-complete.

7. Multiple Instances

rt-smooth-scroll supports any number of independent Lenis instances on a page. Each instance has its own wrapper + content and can be controlled individually via API.

Context Awareness: If a rt-smooth-scroll-to button is placed inside a nested scroll instance, it will automatically control that parent instance, not the root window, unless you explicitly override it with rt-smooth-scroll-target-id.


8. Global API

After initialization, access:

window.rtSmoothScroll;

Common methods:

| Method | Description | | ------------------ | ------------------------------------------------------------------- | | ids() | Array of registered instance ids | | get(id) | Returns Lenis instance | | start(id?) | Start scroll | | stop(id?) | Stop scroll | | toggle(id?) | Toggle scroll | | resize(id?) | Trigger Lenis resize | | refreshAnchors() | Manually re-run anchor link conversion (useful for dynamic content) | | destroy(id?) | Remove instance |

Default root Lenis instance is also exposed as:

window.lenis;

9. Console Logging & Debugging

By default, rt-smooth-scroll runs in debug mode (true). It will log helpful messages to the console during development, such as:

  • Lenis load status
  • Anchor link conversion counts
  • Instance creation (showing resolved options)
  • Scroll triggers (showing target resolution)
  • ResizeObserver attachments

Disabling Logs

To silence these logs in production, set the attribute to false on your root element:

<body rt-smooth-scroll rt-smooth-scroll-debug="false"></body>

10. Troubleshooting

Scroll feels laggy / too delayed

  • Increase rt-smooth-scroll-lerp (e.g. 0.2 → 0.35) for a snappier response.
  • Decrease rt-smooth-scroll-lerp (e.g. 0.1 → 0.05) for a smoother/heavier feel.
  • Leave rt-smooth-scroll-wheel-multiplier="1" unless you have a strong reason to change perceived speed.

Dynamic content (Accordions/Tabs) cuts off scroll

This library includes a built-in ResizeObserver that watches your content for height changes.

  • Ensure your content is properly wrapped.
  • If you have rapid animations causing lag, you can debounce the resize events using rt-smooth-scroll-resize-debounce-ms="100".

My rt-smooth-scroll-on-complete didn’t run

Common causes:

  • The selector/function name is wrong or not available at runtime.
  • Your completion hook depends on DOM that’s not yet mounted/visible.
  • If you are using call:fnName, ensure window.fnName exists before the scroll completes.
  • If you are using JSON, ensure it is valid JSON (double quotes, no trailing commas).

11. License

MIT License

Package: @rethink-js/rt-smooth-scroll GitHub: https://github.com/Rethink-JS/rt-smooth-scroll

by Rethink JS https://github.com/Rethink-JS