react-procedural-scroller
v1.0.2
Published
A headless React hook for building infinite scroller components that display procedurally generated data, perfect for use cases like date scrollers, where row data is generated dynamically rather than fetched from a dataset. Notable features include: full
Maintainers
Readme
A headless React hook for building infinite scroller components that display procedurally generated data. Perfect for use cases like date scrollers, where row data is generated dynamically rather than fetched from a dataset.
- ⚡️Full row/column virtualization for buttery-smooth performance.
- ⤴️ Supports vertical and horizontal layouts.
- 📏 Supports dynamic row/column sizes.
- 🎯 A
scrollToIndexAPI for instant or smooth programmatic scrolling. - 📦 Lightweight ~ 4.95 kB when minified and gzipped.
- 🛠 Zero dependencies.
- 🎭 E2E tested and rigorously vetted for rock-solid reliability.
Quick Start Guide
Follow the steps below to build your first procedural scroller component:
1. Install
Install the library using npm:
npm install react-procedural-scroller2. Import
ES Modules:
import { useProceduralScroller } from "react-procedural-scroller";CommonJS:
const { useProceduralScroller } = require("react-procedural-scroller");3. Build UI Components
This library is headless, meaning it only manages the scrolling state and virtualization logic, giving you full freedom to design your own UI. To get started, you’ll first need to create two components: a scrollable <Container /> element, and an <Item /> component that will be rendered inside the container to form the rows/columns. Each item is identified by an integer index, and you can procedurally generate the item's content based on this index, as shown in the date scroller examples below:
Example container:
import { useProceduralScroller } from "react-procedural-scroller";
import Item from "../components/items/item.tsx";
export default function Container() {
const { items, container } = useProceduralScroller<
HTMLDivElement, // Type of the scrollable container element.
HTMLDivElement // Type of each item inside the container.
>({
// Initial scroll position of the container.
initialScroll: {
index: 0, // Index of the item to scroll to initially.
block: "center", // Alignment of the item in the viewport: "start", "center", or "end".
},
/* Callback to return the minimum size of each item along the relevant axis:
- For vertical scrolling: minimum height.
- For horizontal scrolling: minimum width. */
getMinItemSize: () => 100,
// Direction of scrolling: "vertical" or "horizontal"
scrollDirection: "vertical",
/* Optional: `initialContainerSize` defines the height of the container on the
first page render. Without it, `items` will be null on the first render because the hook
needs to measure the container's size before determining how many items to render.
Providing a value makes `items` available immediately and helps avoid layout shift. */
initialContainerSize: 200,
});
return (
<div
style={{
display: "flex",
flexDirection: "column", // Must match the hook's `scrollDirection`: "column" - vertical, "row" - horizontal.
height: "500px",
width: "500px",
overflow: "scroll", // The container must be scrollable!
border: "2px solid lightgrey",
borderRadius: "10px",
margin: "30px",
scrollbarWidth: 'none',
}}
ref={container.ref} // Enables the hook to track and update the container's scroll position.
>
{items?.map((item) => (
<Item key={item.index} item={item} />
))}
</div>
);
}Example item:
import { memo } from 'react';
import { type Item } from 'react-procedural-scroller';
export default memo(
function Item({ item }: { item: Item<HTMLDivElement> }) {
/* Generate a date relative to today based on the item's index
(e.g., index 0 = today, 1 = tomorrow, -1 = yesterday). */
const date = new Date();
date.setDate(date.getDate() + item.index);
return (
<div
ref={item.ref} // Enables the hook to measure and virtualize this item.
style={{
minHeight: 100, // Must match or exceed the result of getMinItemSize(index) in the container.
borderTop: '1px solid lightgrey',
}}
>
<p
style={{
fontFamily: 'Helvetica',
color: 'grey',
padding: '10px',
}}
>
{date.toDateString()}
</p>
</div>
);
},
/*
* Since an <Item /> component’s content is generated entirely from its index,
* it is highly recommended to wrap it in React.memo with a custom comparison
* function that prevents re-renders unless this index changes.
*/
(prevProps, nextProps) => prevProps.item.index === nextProps.item.index,
);
4. Implement programmatic scrolling
react-procedural-scroller includes a scrollToIndex API, allowing you to scroll to any item programmatically. For example, you can use scrollToIndex to create a “Back to today!” button for the date scroller component from Step 2:
const { items, container, scrollToIndex } = useProceduralScroller(...)<button
onClick={() => {
scrollToIndex({
index: 0,
block: 'center',
behavior: 'smooth',
});
}}
>
Back to today!
</button>5. You're done!
Copying the examples above should result in a date scroller component like this:

