useScrollAnchor
ericts-ui/use-scroll-anchorFreeHook
A client-safe React hook that parks a selected item at a fixed anchor point in a scroll container, with a configurable easing and duration — the click-to-scroll behavior for navigation panels, command palettes, and step lists.
Install it
npx shadcn@latest add https://ui.ericts.com/r/use-scroll-anchor.jsonSource
1"use client";23import { useCallback, useEffect, useLayoutEffect, useRef } from "react";45const DEFAULT_ANCHOR_RATIO = 1 / 3;6const DEFAULT_MIN_DURATION = 160;7const DEFAULT_MAX_DURATION = 320;8const DEFAULT_DISTANCE_DURATION_RATIO = 0.45;9const DEFAULT_TARGET_SELECTOR = "[data-scroll-anchor]";1011const useIsomorphicLayoutEffect =12 typeof window === "undefined" ? useEffect : useLayoutEffect;1314export type ScrollAnchorKey = string | number | null | undefined;15export type ScrollAnchorEasing = (progress: number) => number;1617/** Ready-made `progress → progress` easings to pass as `easing`. */18export const scrollAnchorEasings = {19 easeOutQuart: (progress: number) => 1 - (1 - progress) ** 4,20 easeOutCubic: (progress: number) => 1 - (1 - progress) ** 3,21 easeInOutCubic: (progress: number) =>22 progress < 0.523 ? 4 * progress * progress * progress24 : 1 - (-2 * progress + 2) ** 3 / 2,25 linear: (progress: number) => progress,26} satisfies Record<string, ScrollAnchorEasing>;2728export type UseScrollAnchorOptions<T extends HTMLElement> = {29 /** When this changes while `enabled`, the active target is re-anchored. */30 activeKey: ScrollAnchorKey;31 /** Gate the behavior (e.g. only while a panel is open). Defaults to `true`. */32 enabled?: boolean;33 /**34 * Where the target should land: `0` = top edge, `0.5` = vertical center,35 * `1` = bottom edge. Defaults to `1 / 3` (upper third).36 */37 anchorRatio?: number;38 /**39 * Locate the element to anchor within the container. Defaults to the first40 * `[data-scroll-anchor]` descendant.41 */42 getTarget?: (container: T) => HTMLElement | null;43 /**44 * Ease to the anchor when the key changes. On the first run after enabling —45 * and whenever the user prefers reduced motion — the jump is instant. Defaults46 * to `true`.47 */48 animate?: boolean;49 /** Easing for the animated scroll. Defaults to `scrollAnchorEasings.easeOutQuart`. */50 easing?: ScrollAnchorEasing;51 /**52 * Animation length in ms — a fixed number, or a function of the scroll53 * distance in px. Omit for a distance-proportional ramp (160–320ms).54 */55 duration?: number | ((distance: number) => number);56 /** Jump instantly when the user prefers reduced motion. Defaults to `true`. */57 respectReducedMotion?: boolean;58 /** Fires once the target reaches its anchor (after animating or jumping). */59 onSettled?: () => void;60};6162export type UseScrollAnchorResult<T extends HTMLElement> = {63// … truncated
Files it writes
More from ericts-ui
All 38 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| useElementHeightuse-element-height | ericts-ui | Hooks | Free | no deps | |
| useElementSizeMapuse-element-size-map | ericts-ui | Hooks | Free | no deps | |
| useReducedMotionuse-reduced-motion | ericts-ui | Hooks | Free | no deps | |
| useScrollProgressuse-scroll-progress | ericts-ui | Hooks | Free | no deps |
