Description:
Domet is a lightweight React hook that manages scroll-driven interface logic.
It handles scroll-spy functionality, progress tracking, and section awareness without heavy dependencies.
The library uses a tight scroll loop and hysteresis to track active sections accurately.
Features
- 🧩 Headless Architecture: It exposes logic and state but leaves all styling decisions to you.
- ⚡ Performance Focused: The system uses a throttled scroll loop to prevent layout thrashing.
- 🛡️ Hysteresis Control: It applies a score margin to stop rapid switching between sections.
- 📱 Scroll State Access: The hook tracks global scroll velocity, direction, and progress data.
Preview

Use Cases
- Documentation Sites: Highlight the current chapter in a sidebar navigation as the user reads.
- Single Page Portfolios: Trigger animations when specific project sections enter the viewport.
- Reading Progress Bars: Update a top-aligned progress indicator based on scroll depth.
- Lazy Loading Content: Fetch data or load heavy components only when they approach the visible area.
How to Use It
1. Add the package to your project via npm.
npm install domet2. Import the hook and define the section IDs you want to track. The hook returns props that you spread onto your navigation buttons and content sections.
import { useDomet } from 'domet';
const sectionIds = ['home', 'features', 'pricing', 'contact'];
export default function ScrollSpyNavigation() {
// Initialize the hook with section IDs
const { activeId, navProps, sectionProps } = useDomet(sectionIds);
return (
<div className="layout">
{/* Navigation Bar */}
<nav className="fixed-nav">
{sectionIds.map((id) => (
<button
key={id}
{...navProps(id)}
style={{
fontWeight: activeId === id ? 'bold' : 'normal',
color: activeId === id ? 'blue' : 'black',
}}
>
{id.toUpperCase()}
</button>
))}
</nav>
{/* Content Sections */}
<main>
{sectionIds.map((id) => (
<section
key={id}
{...sectionProps(id)}
style={{ height: '100vh', padding: '20px' }}
>
<h1>{id} Section</h1>
<p>Scroll to see the navigation update.</p>
</section>
))}
</main>
</div>
);
}3. You can access the scroll object to build dynamic effects based on scroll progress or velocity.
const { scroll } = useDomet(sectionIds);
// Example: A progress bar that fills as you scroll down
return (
<>
<div
style={{
position: 'fixed',
top: 0,
left: 0,
height: '4px',
background: 'red',
width: `${scroll.progress * 100}%`,
zIndex: 50
}}
/>
{/* Rest of your app */}
</>
);API Reference
Arguments
| Prop | Type | Default | Description |
|---|---|---|---|
sectionIds | string[] | — | Array of section IDs to track. |
containerRef | RefObject<HTMLElement> | null | null | Scrollable container (defaults to window). |
options | DometOptions | {} | Configuration options. |
Options
| Prop | Type | Default | Description |
|---|---|---|---|
offset | number | 0 | Trigger offset from top in pixels. |
offsetRatio | number | 0.08 | Viewport ratio for trigger line calculation. |
debounceMs | number | 10 | Throttle delay in milliseconds. |
visibilityThreshold | number | 0.6 | Minimum visibility ratio (0-1) for section priority. |
hysteresisMargin | number | 150 | Score margin to prevent rapid section switching. |
behavior | 'smooth' | 'instant' | 'auto' | 'auto' | Scroll behavior. |
Callbacks
| Prop | Type | Description |
|---|---|---|
onActiveChange | (id: string | null, prevId: string | null) => void | Called when active section changes. |
onSectionEnter | (id: string) => void | Called when a section enters the viewport. |
onSectionLeave | (id: string) => void | Called when a section leaves the viewport. |
onScrollStart | () => void | Called when scrolling starts. |
onScrollEnd | () => void | Called when scrolling stops. |
Return Value
| Prop | Type | Description |
|---|---|---|
activeId | string | null | ID of the currently active section. |
activeIndex | number | Index of the active section in sectionIds (-1 if none). |
scroll | ScrollState | Global scroll state (y, progress, direction, velocity). |
sections | Record<string, SectionState> | Per-section state (bounds, visibility, progress). |
sectionProps | (id: string) => SectionProps | Props to spread on section elements. |
navProps | (id: string) => NavProps | Props to spread on nav items. |
registerRef | (id: string) => (el: HTMLElement | null) => void | Manual ref registration. |
scrollToSection | (id: string) => void | Programmatically scroll to a section. |
Related Resources
- React Intersection Observer: Monitors when elements enter or leave the viewport.
- React Scroll: Animates vertical scrolling to specific elements.
FAQs
Q: Does Domet work with custom scroll containers?
A: Yes. You can pass a ref pointing to your scrollable element as the second argument to useDomet.
Q: How does the library prevent flickering when sections are close together?
A: It uses a hysteresisMargin option. This adds a “stickiness” factor to the active section to prevent it from changing too quickly during minor scroll movements.
Q: Can I use this for scroll animations?
A: Yes. The hook returns a scroll object containing velocity, direction, and progress, which you can use to drive animation values.
Q: Is it compatible with Next.js?
A: Yes. Domet works in Next.js applications. Since it relies on window and DOM elements, ensure you use it within Client Components.
