Smooth Drag To Scroll Library – Scrollbooster

Category: Animation , Drag & Drop , Javascript , Recommended | October 9, 2026
Authorilyashubin
Last UpdateOctober 9, 2026
LicenseMIT
Tags
Views5,575 views
Smooth Drag To Scroll Library – Scrollbooster

ScrollBooster is a dependency-free JavaScript library for smooth drag-to-scroll interactions in overflowing content.

It supports mouse, touch, and pen gestures, with momentum and elastic bounce for image galleries, horizontal lists, and scrollable panels.

Features:

  • Horizontal, vertical, and two-axis scrolling.
  • Wheel, trackpad, and keyboard input.
  • Adjustable friction and edge bounce.
  • Optional snap positions for carousels and paging.
  • Native scrolling and custom rendering modes.
  • Right-to-left layouts, nested scrollers, and reduced-motion settings.

How to use it:

Install ScrollBooster

Install the npm package and import ScrollBooster.

npm install scrollbooster
import { ScrollBooster } from 'scrollbooster';

You can also load the browser build before the initialization code. It exposes the ScrollBooster constructor globally.

<script src="https://unpkg.com/scrollbooster@4/dist/scrollbooster.min.js"></script>

HTML and CSS

Place the scrollable content inside a viewport element. ScrollBooster uses the first child as its content by default.

<div class="sb-viewport" tabindex="0" aria-label="Scrollable gallery">
  <div class="sb-content">
    <article class="sb-item">Panel One</article>
    <article class="sb-item">Panel Two</article>
    <article class="sb-item">Panel Three</article>
    <article class="sb-item">Panel Four</article>
  </div>
</div>

Clip the viewport and make the inner row wider than the viewport to create horizontal overflow.

.sb-viewport {
  max-width: 100%;
  overflow: hidden;
}
.sb-content {
  display: flex;
  width: max-content;
  gap: 16px;
}
.sb-item {
  box-sizing: border-box;
  flex: 0 0 220px;
  padding: 24px;
  background: #f1f5f9;
}

Basic Usage

Initialize ScrollBooster. The default scrollMode: 'transform' moves the content automatically.

const viewport = document.querySelector('.sb-viewport');
const sb = new ScrollBooster({
  viewport,
  direction: 'horizontal',
});

Scroll modes

ScrollBooster renders scrolling automatically in transform mode. For a browser-native scroll container, use native. Choose none when you need to update CSS properties yourself in onUpdate.

ModeBehavior
transform (default)Moves the inner content through the CSS translate property. Keeps an existing transform and supports elastic bounce. Browser scroll events and sticky elements inside the translated content do not behave like native scrolling.
nativeUpdates the viewport’s scrollLeft and scrollTop. Use CSS overflow: auto on the viewport. Keeps native browser scrolling behavior but does not bounce.
noneLeaves position rendering to your onUpdate callback.

Custom rendering with onUpdate

To render scrolling yourself, replace the basic initializer with this one. It uses scrollMode: 'none' and a left-to-right CSS transform. Set will-change: transform on .sb-content for this mode.

.sb-content {
  will-change: transform;
}
const viewport = document.querySelector('.sb-viewport');
const content = viewport.querySelector('.sb-content');
const sb = new ScrollBooster({
  viewport,
  content,
  scrollMode: 'none',
  onUpdate: ({ position }) => {
    content.style.transform = `translate(${-position.x}px, ${-position.y}px)`;
  },
});

More usage

Horizontal galleries and mouse wheel scrolling

For a horizontal gallery, vertical touch swipes can scroll the page. To make an ordinary vertical mouse wheel move the gallery horizontally, replace the basic initializer with this configuration.

const sb = new ScrollBooster({
  viewport: document.querySelector('.sb-viewport'),
  direction: 'horizontal',
  wheel: 'horizontal',
  bounce: true,
  friction: 0.05,
});

Snap a horizontal carousel to each item

For fixed-width items, the snap interval must include the gap between items. The earlier CSS uses 220px-wide items with a 16px gap, so the interval is 236px. Place two buttons outside the viewport and replace the basic initializer with this example.

<button type="button" id="carousel-prev">Previous</button>
<button type="button" id="carousel-next">Next</button>
const step = 236;
const sb = new ScrollBooster({
  viewport: document.querySelector('.sb-viewport'),
  direction: 'horizontal',
  snap: (rest) => ({
    x: Math.round(rest.x / step) * step,
  }),
});
document.querySelector('#carousel-prev').addEventListener('click', () => {
  sb.scrollBy({ x: -step });
});
document.querySelector('#carousel-next').addEventListener('click', () => {
  sb.scrollBy({ x: step });
});

Keep native scrolling on touch devices

To keep normal touch scrolling and browser scroll behavior, set overflow: auto on the viewport and replace the basic initializer with this configuration. Mouse and pen dragging continue to work.

const sb = new ScrollBooster({
  viewport: document.querySelector('.sb-viewport'),
  scrollMode: 'native',
  pointerMode: 'mouse',
  direction: 'horizontal',
});

Configuration Options

Pass the options to new ScrollBooster({ ... }) or update an existing instance with updateOptions().

Container, direction, and input

OptionType, default, and description
viewportHTMLElement, required. Outer element that displays the scrolling content.
contentHTMLElement, default: first child of the viewport. Must be inside the viewport.
direction'all' | 'horizontal' | 'vertical', default 'all'. Allowed scrolling directions.
scrollMode'transform' | 'native' | 'none', default 'transform'. Scroll rendering mode.
pointerMode'all' | 'touch' | 'mouse', default 'all'. Pointer types accepted for dragging. 'mouse' also includes pen.
axisLockboolean, default false. Locks a gesture to the axis where it begins when both axes are enabled.
textSelectionboolean, default false. Permits mouse or pen text selection on a press. Touch dragging works on text, and a long touch press can select a word.
inputsFocusboolean, default true. Excludes form controls, editable content, summaries, and media controls from drag initiation.
wheeltrue | false | 'horizontal', default true. Enables wheel and trackpad scrolling. 'horizontal' maps vertical wheel gestures to horizontal movement.
keyboardboolean, default true. Enables arrow, Page Up/Down, Space, Home, and End navigation while focus is inside the viewport.

Motion and snapping

The friction and bounceForce factors must be greater than 0 and less than 1. They are defined per 60 Hz frame, and scrolling physics is normalized across display refresh rates.

OptionType, default, and description
bounceboolean, default true. Enables elastic resistance and bounce beyond content edges in transform mode.
frictionnumber, default 0.05. Controls the rate at which movement slows after release.
bounceForcenumber, default 0.1. Controls how quickly overscrolled content returns to an edge.
reducedMotion'auto' | 'always' | 'never', default 'auto'. Follows prefers-reduced-motion, always reduces motion, or ignores the preference. Reduced motion disables inertia and bounce and makes scrollTo() immediate.

Callbacks

Callbacks receive the current scroll state and, when applicable, a DOM event.

OptionArguments and behavior
onUpdate(state) => void. Runs on animation frames when scroll state changes, including the first frame after creation.
onClick(state, event) => void. Runs for clicks in the viewport. Check event.defaultPrevented to identify a click suppressed after dragging.
onPointerDown(state, event) => void. Runs when a permitted pointer press begins a drag interaction.
onPointerMove(state, event) => void. Runs while the pointer drags content.
onPointerUp(state, event) => void. Runs when the active pointer is released or cancelled.
onWheel(state, event) => void. Runs for wheel events consumed by the content.
shouldDrag(state, event) => boolean. Return false to prevent a pointer press from starting a drag.
snap(rest, state) => { x?, y? } | undefined. Returns the desired snap position after dragging or a wheel gesture. Return undefined to keep normal inertia.

API Methods

MethodDescription
scrollTo({ x, y })Smoothly scrolls to a position. Does nothing during an active pointer drag.
scrollBy({ x, y })Smoothly scrolls by a relative distance. Repeated calls accumulate from the running destination.
scrollIntoView(element, { align })Scrolls to show an element inside the content. align accepts 'nearest' (default), 'start', 'center', or 'end'.
setPosition({ x, y })Jumps to a position and stops ongoing motion.
getState()Returns the current scroll state object.
updateOptions(options)Changes any validated option, including viewport and content elements.
updateMetrics()Recalculates sizes and bounds. ResizeObserver handles element size changes automatically.
destroy()Stops motion, releases listeners, and restores styles changed by the instance.

Scroll State Properties

Use getState() to read the current state at any time. Each call returns a new object. In right-to-left content, position.x measures the distance from the right edge and increases toward the left.

PropertyDescription
position{ x, y }. Current scroll position measured from the starting edges.
maxPosition{ x, y }. Maximum scroll position on each axis.
viewport{ width, height }. Viewport dimensions.
content{ width, height }. Scrollable content dimensions.
isMovingboolean. Indicates that content is moving. The last update of a motion has false.
isDraggingboolean. Indicates movement beyond the drag threshold while the pointer is pressed.
dragOffset{ x, y }. Pointer displacement during the current drag. Resets after release.
dragAnglenumber. Angle of the current drag. Resets after release.
borderCollision{ left, right, top, bottom }. Booleans that describe which viewport edges the content touches.

Upgrading from ScrollBooster 3.x

Version 4 removes several v3 settings and changes the default rendering behavior. Passing a removed option throws TypeError.

Version 3Version 4
import ScrollBooster from 'scrollbooster'import { ScrollBooster } from 'scrollbooster'. The default export is removed.
shouldScrollUse shouldDrag for pointer drag permission.
emulateScroll and preventDefaultOnEmulateScrollUse wheel, which defaults to true.
lockScrollOnDragDirection, preventPointerMoveDefault, pointerDownPreventDefault, dragDirectionToleranceRemoved. Configure direction and CSS touch behavior as appropriate.
scrollMode could be left unset for custom renderingDefault is 'transform'. Set scrollMode: 'none' for your own onUpdate renderer.
Mouse and touch callbacks used different event types and an optional isTouch parameterPointer callbacks receive PointerEvent. Check event.pointerType.
Content repositioned through the transform propertyBuilt-in transform mode uses CSS translate, preserving an existing transform.
Older browsers including IE11Chrome/Edge 104+, Firefox 90+, Safari/iOS Safari 15+. Version 3.x is required for IE11.

FAQs

Q: Why does initialization fail when the viewport has no child element?
A: ScrollBooster requires an HTML content element inside the viewport. Insert a child element or pass an existing descendant through the content option. Invalid element references throw TypeError.

Q: Why does onUpdate not run immediately after creating the instance?
A: The first onUpdate callback runs on the next animation frame. Call getState() if you need the current state immediately after initialization.

Q: How do I prevent dragging from a custom interactive element?
A: Use shouldDrag to return false when the pointer starts on that element. The default inputsFocus: true already protects form controls and editable content.

Q: Why do sticky elements and scroll event listeners behave differently?
A: In the default transform mode, ScrollBooster translates the content instead of changing the viewport’s native scroll position. Use scrollMode: 'native' with overflow: auto for native scroll behavior.

Q: Why does the content return to its initial position after destroy()?
A: In transform mode, destroy() removes the translation applied by ScrollBooster. Save getState().position before cleanup, then call setPosition() on a new instance to restore that position.

Alternatives & Related Resources

Changelog:

v4.0.0 (10/09/2026):

  • Introduced Pointer Events, snap points, keyboard controls, reduced-motion settings, two new scrolling methods, and updated package exports. Several v3 options were removed.

You Might Be Interested In:


Leave a Reply