af-utils / virtual

Headless API Reference

Headless API is the core of the library and it provides maximum flexibility. You can work with it directly or delegate rendering to List or Table.


# npm
npm i -S @af-utils/react-virtual-headless prop-types use-sync-external-store

# yarn
yarn add @af-utils/react-virtual-headless prop-types use-sync-external-store


Core hook. Used to get scroller model instance synchronized with props. This instance will persist for the full lifetime of the component.

const model = useVirtual({

itemCount: Integer

Quantity of items. Defaults to 0.

estimatedItemSize: Integer

Best-guess item size. Defaults to 40.

overscanCount: Integer

Minimum amount of items to load behind/ahead(depends on scroll direction) of the current visible range. Defaults to 3. Example.

horizontal: Boolean

Determines, whether scrollTop/offsetHeight or scrollLeft/offsetWidth are used in calculations. Defaults to false.

estimatedWidgetSize: Integer

Widget size to use before ResizeObserver measurements are ready. Used mainly for server-side rendering. Defaults to 200.


Scroller model instance with following public props:

from: Integer

Items range start.

to: Integer

Items range end.

scrollSize: Integer

Sum of all item sizes.

el: Function(index: Integer, element: Element) => void

Start observing size of element at index. Observing is finished if element is falsy.

setStickyHeader: Function(element: Element) => void

Start observing size of sticky header element. Observing is finished if element is falsy.

setStickyFooter: Function(element: Element) => void

Start observing size of sticky footer element. Observing is finished if element is falsy.

getIndex: Function(offset: Integer) => Integer

Get element index pixel offset.

getOffset: Function(index: Integer) => Integer

Get pixel offset for element index.

getSize: Function(index: Integer) => Integer

Get cached size for element at index.

visibleFrom: Getter() => Double

Get snapshot of current scroll position. For example 5.3 stands for element at index 5 + 30% of its height. Used to remember scroll position before prepending elements. Example.

scrollTo: Function(index: Double, smooth: Boolean ) => void

Scroll to element at certain index. For example 3.6 will scroll to element at index 3 plus 60% of its height.

setOuterNode: Bound Function(el: DOMElement) => void

Pass scrollable element to model. Must be attached to outermost element with overflow: auto.

set: Function(params: Object) => void

Manually updates model params. Should be used ONLY with useVirtualModel. Accepts all params of useVirtual except estimatedWidgetSize.


Low-level hook, used by useVirtual internally. Accepts same params, but without synchronization with props. Usage example. Difference and interconnection between these 2 hooks:

import { useLayoutEffect } from "react";
import useVirtualModel from "../useVirtualModel";

const useVirtual = params => {
    const model = useVirtualModel(params);

    useLayoutEffect(() => model.set(params));

    return model;


mapVisibleRange(model, callback, provideOffset);

model: Model

Required. Return value of useVirtual.

callback: Function( index, offset ) => ReactElement

Required. Function called for each visible element index.

countOffset: Boolean

Should provide offset to callback or not. Needed for lists, where each item is absolutely positioned. Defaults to: false.



Numeric event constants

Scroller model has build-in event emitter. All possible events are exported.


    Visible range (model.from or changed. Used to make load on demand.


    Sum of item sizes changed.


    At least one item size changed. Used, when all items are position: absolute and are not automatically realigned when siblinbgs are resized.

  • EVT_ALL(array of events above)


Called, when at least one of passed events is triggered.

useSubscription(model, events, callback);

model: Model

Required. Return value of useVirtual.

events: Array<Integer>

Required. Array of events to subscribe

callback: Function() => void

Gets called when at least one of events gets triggered. In case you do not want to subscribe - pass null.


Rerendered, when at least one of passed events is triggered. Example.

<Subscription model={model} events={events}>
    {() => {}}

model: Model

Required. Return value of useVirtual.

children: Function() => ReactElement

Required. Render prop.

events: Array<Integer>

Required. Defaults to: EVT_ALL