Efficient, single direction virtual list/grid for Solid-js
- Support for grid/list modes.
- Only render visible items, no matter how big is your list.
- Keyboard navigation and focus management out of the box.
- Option to change items size based on available space.
npm install @minht11/solid-virtual-container
Create list item component.
constListItem=(props)=>(<div// Required for items to switch places.style={props.style}// Use CSS to set width to 100% or any other value.class='width-full'// Used for keyboard navigation and accessibility.tabIndex={props.tabIndex}role="listitem"><div>{props.item}</div></div>)Create vertically scrolling virtual list
import{VirtualContainer}from"@minht11/solid-virtual-container"constApp=()=>{constitems=[0,1,2,3]letscrollTargetElement!: HTMLDivElementreturn(<divstyle={{overflow: 'auto'}}ref={scrollTargetElement}><VirtualContaineritems={items}scrollTarget={scrollTargetElement}// Define size you wish your list items to take.itemSize={{height: 50}}>{ListItem}</VirtualContainer></div>)}or a virtual grid
constApp=()=>{constitems=[0,1,2,3]letscrollTargetElement!: HTMLDivElementreturn(<divstyle={{overflow: 'auto'}}ref={scrollTargetElement}><VirtualContaineritems={items}scrollTarget={scrollTargetElement}itemSize={{height: 50,width: 50}}// Calculate how many columns to show.crossAxisCount={(measurements)=>(Math.floor(measurements.container.cross/measurements.itemSize.cross))}>{ListItem}</VirtualContainer></div>)}You can control list items styling using regular CSS, including
widthandheight, however properties defined usingitemSizewill take a priority.
One dimensional lists require only main direction size be set using
itemSize. For vertical scrolling that'sheightand for horizontal direction that'swidth. Multidimensional lists require both.
If you you do not have an immediate access to the VirtualContainer, or do not want to pass props several components deep you can use context api.
constApp=()=>{constitems=[0,1,2,3]letscrollTargetElement!: HTMLDivElementreturn(<divref={scrollTargetElement}><ScrollTargetContext.Providervalue={{scrollTarget: scrollTargetElement}}>
...
<VirtualContainer.../>
...
</ScrollTargetContext.Provider></div>
)
}interfaceVirtualContainer<T>{// your list data array. items: readonlyT[]// Define elements size.// All elements will use same size.itemSize: VirtualItemSize// Scrolling element, if context api is used this is not needed,// however you must use one or the other.scrollTarget?: HTMLElement// Scroll direction. Default is vertical.direction?: 'vertical'|'horizontal'// Number of elements to render below and above currently visible items,// if not provided an optimal amount will be automatically picked.overscan?: number// Container className, if at all possible ignore this option,// because direct styling can break virtualizing, instead wrap// element in another div and style that.className?: stringrole?: JSX.HTMLAttributes<HTMLDivElement>['role']// Function which determines how many columns in vertical mode// or rows in horizontal to show. Default is 1.crossAxisCount?: (measurements: CrossAxisCountOptions,// The same as items.lengthitemsCount: number,)=>number// List item render function.children: (props: VirtualItemProps<T>)=>JSX.Element}If direction is vertical main axis is vertical.
If direction is horizontal main axis is horizontal.
interfaceAxis{// Main scrolling direction axis.main: number// Opposite axis to main.cross: number}Parameter object used in VirtualContainer.crossAxisCount function.
interfaceCrossAxisCountOptions{// Scrolling element dimensions.target: Axis// Container element dimensions.container: Axis// List element dimensions.itemSize: Axis}// You can use static object to define item sizeinterfaceVirtualItemSizeStatic{width?: numberheight?: number}// or use a function to calculate it when layout changes.typeVirtualItemSizeDynamic=(crossAxisContentSize: number,// Scroll direction.isHorizontal: boolean,)=>VirtualItemSizeStaticDynamic size is useful when you want your grid items to fill all available space
// One possible exampleconstcalculateItemSize=(crossAxisSize: number)=>{// Choose minimum size depending on the available space.constminWidth=crossAxisSize>560 ? 180 : 140constcount=Math.floor(crossAxisSize/minWidth)constwidth=Math.floor(crossAxisSize/count)return{
width,height: width+48}}<VirtualContentitemSize={calculateItemSize}></VirtualContent>Different individual item sizes and scrolling with both directions at the same time are not and likely will never be supported by this package.
Page CSP must allow inline style sheets.