Skip to main content
Solid Primitives 2

Reactive primitives to react to element/window scrolling, and to prevent scroll outside of a given element.

StageCategoryVersionLast UpdatedDemo
3Inputs3.0.0-next.4 (next)Aug 12, 2026Demo →
Terminal window
npm i @solid-primitives/scroll@next

Reactive primitives to react to element/window scrolling, and to prevent scroll outside of a given element.

createScrollPosition

Reactive primitive providing a store-like object with current scroll position of specified target.

How to use it

import { createScrollPosition } from "@solid-primitives/scroll";
// target will be window by default
const windowScroll = createScrollPosition();
createEffect(() => {
// returned object is a reactive store-like structure
windowScroll.x; // => number
windowScroll.y; // => number
});

With element refs

let ref: HTMLDivElement | undefined;
// pass as function — preferred, handles ref population automatically
const scroll = createScrollPosition(() => ref);
// or wrap with onSettled
onSettled(() => {
const scroll = createScrollPosition(ref!);
});
<div ref={e => (ref = e)} />;

Reactive Target

The element target can be a reactive signal.

const [target, setTarget] = createSignal<Element | undefined>(element);
const scroll = createScrollPosition(target);
// if target is undefined, scroll values will be 0
scroll.x; // => number
scroll.y; // => number
// update the tracking element
setTarget(ref);
// disable tracking
setTarget(undefined);

Destructuring

If you are interested in listening to only single axis, you'd still have to access scroll.y as a property. To use it as a separate signal, you can wrap it with a function, or use destructure helper.

const scroll = createScrollPosition();
const x = () => scroll.x;
x(); // => number
// or destructure
import { destructure } from "@solid-primitives/destructure";
const { x, y } = destructure(createScrollPosition());
x(); // => number
y(); // => number

useWindowScrollPosition

Returns a reactive object with current window scroll position.

useWindowScrollPosition is a singleton root primitive, hence the object instance, signals and event-listeners are shared between dependents, making it more optimized to use in multiple places at once.

const scroll = useWindowScrollPosition();
createEffect(() => {
console.log(
scroll.x, // => number
scroll.y, // => number
);
});

Additional helpers

getScrollPosition

Get an { x: number, y: number } object of element/window scroll position.

createPreventScroll

Prevents scrolling outside of the given element by intercepting wheel and touchmove events and optionally hiding the <body> scrollbar.

Adapted from solid-prevent-scroll by Jasmin Noetzli (GiyoMoon), part of the corvu project, which is itself inspired by react-remove-scroll by Anton Korzunov. Adapted for Solid 2.0 and solid-primitives conventions.

How to use it

import { createPreventScroll } from "@solid-primitives/scroll";
// Prevent all page scroll (no element specified)
createPreventScroll();
// Prevent scroll outside a specific element
createPreventScroll({ element: () => myElement });
// Using a signal ref — preferred pattern for JSX refs
const [ref, setRef] = createSignal<HTMLElement>();
createPreventScroll({ element: ref });
<div ref={setRef} />;
// Reactive enabled toggle
const [open, setOpen] = createSignal(false);
createPreventScroll({ enabled: open });

Props

PropTypeDefaultDescription
elementMaybeAccessor<HTMLElement | undefined>undefinedAllow scroll inside this element. Events outside it are cancelled.
enabledMaybeAccessor<boolean>trueWhether scroll prevention is active.
hideScrollbarMaybeAccessor<boolean>trueHide the <body> scrollbar while active.
preventScrollbarShiftMaybeAccessor<boolean>trueCompensate for the hidden scrollbar width to avoid layout shift.
preventScrollbarShiftModeMaybeAccessor<"padding" | "margin">"padding"Which CSS property to use for the scrollbar shift compensation.
restoreScrollPositionMaybeAccessor<boolean>trueRestore <body> scroll position via window.scrollTo when disabled.
allowPinchZoomMaybeAccessor<boolean>falseAllow two-finger pinch-zoom gestures.

Multiple active instances are stacked; only the topmost one installs event listeners. Body styles are shared and only restored once all instances clean up.

Primitive ideas:

PRs Welcome :)

  • createScrollTo - A primitive to support scroll to a target
  • createHashScroll - A primitive to support scrolling based on a hashtag change

Changelog

See CHANGELOG.md

Solid Primitives 2High-quality reactive primitives for building applications in Solid2
Community
githubdiscord