Version 10 is a modernization of react-virtualized. The source has been converted to TypeScript, every component has been rewritten as a function component with hooks, and React 18 or newer is required. Along the way, several long-standing bugs that were inherent in the 9.x architecture have been fixed (see "Bug fixes" below), which required some breaking API changes. The migration guide below covers each of them.
Migrating from 9.x
1. Update React. React 18 or newer is required (react and react-dom ^18.0.0 || ^19.0.0).
2. Import from the package root. Deep imports (react-virtualized/dist/commonjs/List, react-virtualized/dist/es/List) and the UMD build are gone; bundlers tree-shake unused components.
// Before
import List from 'react-virtualized/dist/commonjs/List';
// After
import {List} from 'react-virtualized';3. Replace AutoSizer with the react-virtualized-auto-sizer package (2.x). Width and height are undefined until measured, so disableHeight/disableWidth/defaultHeight/defaultWidth are no longer needed:
// Before
import {AutoSizer, List} from 'react-virtualized';
<AutoSizer>{({height, width}) => <List height={height} width={width} {...props} />}</AutoSizer>
// After
import {AutoSizer} from 'react-virtualized-auto-sizer';
import {List} from 'react-virtualized';
<AutoSizer
renderProp={({height, width}) =>
height !== undefined && width !== undefined ? (
<List height={height} width={width} {...props} />
) : null
}
/>4. Replace controlled scroll props with ref methods. scrollToIndex, scrollToRow, scrollToColumn, scrollToAlignment, scrollTop, and scrollLeft are no longer props:
// Before
<List scrollToIndex={index} scrollToAlignment="center" {...props} />
// After
const listRef = useRef(null); // useRef<ListImperativeAPI>(null)
<List ref={listRef} {...props} />
listRef.current.scrollToRow({index, align: 'center'});Use scrollToCell({columnIndex, rowIndex, align}) for Grid/MultiGrid and scrollToPosition({scrollLeft, scrollTop}) for offsets. These methods are one-time scrolls: unlike the props, the target isn't re-applied when sizes or counts change afterwards. To scroll to a row that is being added, call scrollToRow once it has rendered (e.g. in an effect); rows not yet measured by CellMeasurer are positioned using their estimated size. List/Table scrollToRow(index) and scrollToPosition(scrollTop) now take objects: scrollToRow({index}), scrollToPosition({scrollTop}). To start at a scroll position (e.g. one restored from the URL), pass initialScrollTop/initialScrollLeft instead of scrollTop/scrollLeft; they are only read when the component mounts (and also work with server rendering).
5. Use refs as imperative handles. Components are function components, so refs no longer point to component instances. Type them with the exported *ImperativeAPI types (e.g. useRef<GridImperativeAPI>(null)); internals such as list.Grid are no longer reachable. The documented grid.forceUpdate() pattern still works through the handle: gridRef.current.forceUpdate() (or forceUpdateGrid() for List/Table). Static defaultProps are replaced by exported objects such as defaultGridProps and defaultColumnProps.
6. Update ScrollSync and ArrowKeyStepper children. Instead of passing scrollLeft/scrollTop (or scrollToColumn/scrollToRow) to the Grid, attach the registerChild ref callback:
<ScrollSync>
{({onScroll, registerChild}) => (
<>
<Grid ref={registerChild('header')} {...headerProps} />
<Grid ref={registerChild('body')} onScroll={onScroll} {...bodyProps} />
</>
)}
</ScrollSync>
<ArrowKeyStepper columnCount={columnCount} rowCount={rowCount}>
{({onSectionRendered, registerChild, scrollToColumn, scrollToRow}) => (
// scrollToColumn/scrollToRow are the active cell (e.g. to highlight it)
<Grid ref={registerChild} onSectionRendered={onSectionRendered} {...props} />
)}
</ArrowKeyStepper>7. Replace WindowScroller with the scrollElement prop. WindowScroller has been removed. Instead, pass scrollElement="window" (or a scrollable element) to a Grid, List, Masonry or Table. The component then renders at its full height, renders only the rows visible within the scroll element's viewport, and its scrollToRow/scrollToCell/scrollToPosition methods scroll the scroll element. height is optional: the viewport height of the scroll element is used.
// Before
<WindowScroller>
{({height, isScrolling, onChildScroll, registerChild, scrollTop}) => (
<div ref={registerChild}>
<List autoHeight height={height} isScrolling={isScrolling} onScroll={onChildScroll} scrollTop={scrollTop} width={width} {...props} />
</div>
)}
</WindowScroller>
// After
<List ref={listRef} scrollElement="window" width={width} {...props} />registerChildand the wrapper element: not needed; use a regularrefto call the component's methods.height/widthrender params: the viewport height is measured by the component; useAutoSizer(renderProp={({width}) => …}) for the width.updatePosition()(andupdateScrollTopOnUpdatePosition): not needed; the component's position within the scroll element is measured again on every scroll, on resize, and whenever it re-renders. If content above it changes size without it re-rendering, callforceUpdateGrid()(List/Table) orforceUpdate()(Grid/Masonry).scrollElement={element}: pass the element itself (e.g. stored with auseStateref callback);nulluntil it is available (the component then renders its top rows).onScroll: use the component's ownonScroll.onResize: listen to the window'sresizeevent (or observe the scroll element).serverHeight/serverWidth: passheight(used as the viewport height until the scroll element has been measured, e.g. on the server) andwidth.scrollingResetTimeInterval: use the prop of theGrid,List,MasonryorTable.
8. Replace Column subclasses with wrapper components. Table applies defaultColumnProps to any child element, so const MyColumn = (props) => <Column {...props} /> works as a column.
9. Make sure CellMeasurer children can be measured. The child must be a DOM element, or a component that passes its ref to one (a regular prop in React 19, forwardRef in React 18). Otherwise use the registerChild render prop. A DEV warning is logged when there is nothing to measure.
10. Remove Collection. It has been removed; there is no direct replacement.
Breaking changes
- React 18 or newer is required (
reactandreact-dompeer dependencies are^18.0.0 || ^19.0.0). - The package ships a single ES module (
dist/react-virtualized.js) and a single CommonJS module (dist/react-virtualized.cjs), with anexportsmap. Deep imports and the UMD build (dist/umd/react-virtualized.js) are no longer supported. - Components no longer define
propTypes; prop validation is done by TypeScript. AutoSizerhas been removed; usereact-virtualized-auto-sizer2.x (renderProp/ChildComponentreplace thechildrenfunction).Collection(and itsReactVirtualized__Collection*styles) has been removed.- All components are function components wrapped in
memo; refs expose imperative handles (*ImperativeAPItypes) instead of component instances, and staticdefaultPropsare replaced by exporteddefault*Propsobjects. Subclassing components is no longer supported. - Controlled scroll props have been removed:
scrollToRow,scrollToColumn, andscrollToAlignmentfromGrid;scrollToIndexandscrollToAlignmentfromListandTable;scrollLeft,scrollTop,scrollToRow,scrollToColumn, andscrollToAlignmentfromMultiGrid. To render (or server-render) at an initial scroll position, use the newinitialScrollTop/initialScrollLeftprops. List/Table:scrollToRow(index)is nowscrollToRow({ index, align })andscrollToPosition(scrollTop)is nowscrollToPosition({ scrollTop }).scrollToPositionignores unchanged offsets and clamps to the scrollable range.Grid'sgetOffsetForCell()andList/Table'sgetOffsetForRow()default to the last cell (or row) and"auto"alignment; they used to default to thescrollToColumn/scrollToRowprops, which no longer exist.List's props no longer include theGridprops that don't apply to a single-column list (estimatedColumnSize,overscanColumnCount).- The
parentpassed toList'srowRenderer,Table'scellRenderer,Masonry'scellRenderer, and everyMultiGridquadrant'scellRendereris now that component's imperative handle. WindowScrollerhas been removed, along withdefaultWindowScrollerProps,IS_SCROLLING_TIMEOUTand theWindowScroller*types.Grid,List,MasonryandTabletake ascrollElementprop instead (see the migration guide).updatePosition()is replaced by automatic re-measuring: a component's position within its scroll element is measured on every scroll, on resize, and after every render.- The
autoHeight,autoWidth,scrollTop,scrollLeftandisScrollingprops have been removed fromGrid,List,Table,MasonryandMultiGrid. They only existed to connect a component toWindowScroller; use thescrollElementprop instead. Horizontal window scrolling (autoWidthwithscrollLeft) is no longer supported (aGridwith ascrollElementstill scrolls horizontally itself). - With a
scrollElement,heightis optional (the scroll element's viewport height is used;heightis only a fallback until it has been measured, e.g. during server rendering), andinitialScrollTopis ignored. Without either,Grid,List,TableandMasonrylog a DEV warning (and render nothing). ArrowKeyStepperandScrollSyncdrive their children throughregisterChildinstead of controlled scroll props.ArrowKeyStepperscrolls the active cell into view when it changes (rather than re-applying it on everyGridupdate).CellMeasurerchildren must accept aref(or useregisterChild); cells with no DOM node to measure keep the cache's default size instead of being cached as 0×0.InfiniteLoaderre-renders its registered child with the first method it implements:recomputeGridSize,recomputeRowHeights,forceUpdateGrid, thenforceUpdate. Custom function-component children must expose one of these through their imperative handle.- Size changes of a
scrollElementelement are detected withResizeObserver(window scrolling used to inject resize-trigger elements and a<style>tag, and setposition: relativeon the scroll element; none of this happens anymore). - A non-
Columnchild ofTableno longer throws; it is treated as a column with the default column props. CellMeasurerCachederives column widths and row heights from measured cells only, so a column (or row) can now be smaller thandefaultWidth/defaultHeightonce any of its cells have been measured (see "Bug fixes").Listrenders a list rather than a single-column grid (#1657): itsroledefaults to"list", rows without arolegetrole="listitem", the rows' container getsrole="presentation", andaria-label(which defaulted to"grid") andaria-readonlyare no longer set by default.defaultListPropshasroleinstead of"aria-label". Passrole="grid"for the version 9 roles.- With
align: "auto"(the default),scrollToRow,scrollToCell(andArrowKeyStepper) align a row or cell that is larger than the viewport to its start, instead of its end (#1263).
New
- TypeScript types for every component's props (e.g.
GridProps,ListProps,TableProps,ColumnProps), imperative handles (*ImperativeAPI), and renderer params (e.g.RowRendererParams,CellRendererParams,TableCellProps).ListPropsandTablePropsinclude theGridprops they relay to their innerGrid(e.g.isScrollingOptOut,scrollingResetTimeInterval,containerStyle), except those that don't apply to a single column (e.g.overscanColumnCount). Grid's handle addsforceUpdate();scrollToCelltakes analignparameter.List,Table, andMultiGridaddscrollToCell();MultiGridaddsscrollToPosition();MasonryaddsforceUpdate(),recomputeGridSize(), andinvalidateCellSizeAfterRender()(so it works withInfiniteLoaderandCellMeasurer'smeasure()).scrollElementprop (Grid,List,Masonry,Table):"window"or a scrollable element that scrolls the component instead of the component scrolling itself (replacesWindowScroller). The component renders at its full height, renders only the rows within the scroll element's viewport, and its scroll methods scroll the scroll element. NewScrollElementtype. See https://react-virtualized-docs.vercel.app/window-scrollinginitialScrollTop(Grid,List,Table,MultiGrid) andinitialScrollLeft(Grid,MultiGrid) props render a component at an initial scroll offset, including during server rendering: the rows at that offset are rendered on the server and stay in place when the page is hydrated. See the new server rendering guide.List/TablegetOffsetForRowacceptsalign(alignmentis deprecated).Table'sheaderHeightprop is optional (it already defaulted to 0).- New type exports:
ScrollElement,ScrollSyncChild,ScrollSyncChildProps,ScrollSyncScrollParams,ArrowKeyStepperChild,ArrowKeyStepperChildProps,InfiniteLoaderChildInstance,CellMeasurerChildParams,CellMeasurerChildFunction,CellMeasurerCacheAPI(the public methods and properties ofCellMeasurerCache). - The build is no longer minified (component names show up in React DevTools), and unused components are tree-shaken.
- The
react-lifecycles-compat,prop-types,@babel/runtime, andloose-envifydependencies have been removed. The stylesheet is still available asreact-virtualized/styles.css. - New documentation site, with live examples: https://react-virtualized-docs.vercel.app/
Bug fixes
- Fix
Tablecrashing under React 19 (e.g. "headerRenderer is not a function") when aColumnomitted props that have default values. React 19's JSX runtime no longer applies a class component'sdefaultPropsuntil it renders, andTablereadsColumnprops without rendering them. Grid.measureAllCells(),List.measureAllRows(),Table.measureAllRows()andMultiGrid.measureAllCells()no longer throw when there are no rows or columns (or, forMultiGrid, when every row or column is fixed).Masonry: callingcellPositioner.reset()without aspacernow defaultsspacerto 0 instead of producingNaNpositions.Masonryno longer treats a column with a height of 0 as unset, which could stop measuring early and leave gaps.Masonryno longer renders cells at indices ≥cellCountaftercellCountshrinks, andrecomputeCellPositions()no longer recomputes positions for removed cells.createTableMultiSortno longer mutates thedefaultSortByarray passed to it, so several tables can share the same default. It also creates newsortByandsortDirectionvalues whenever the sort criteria change (instead of mutating them in place), so storing them in React state (e.g. withuseState) re-renders theTable(#1585). The returned object'ssortByandsortDirectionalways refer to the latest values; a reference kept to an earlier value no longer changes.MultiGrid:CellMeasurerCacheDecorator'scolumnWidth()androwHeight()now return the measured size instead ofundefined.Grid: cells whose size getter returnsnull(not yet measured) after the last measured cell are no longer marked as measured, and a size getter that throws no longer corrupts the cached measurements.InfiniteLoadernow recomputesGrid/Listsizes starting from the newly loaded row instead of from row 0.Gridworks with React's StrictMode and concurrent rendering (its derived state was mutated during render).Gridno longer renders stale cell styles afterrowCount/columnCountchange while the size getter stays the same.GridwithisScrollingOptOutre-renders visible cells when props change orforceUpdate()is called.CellMeasurermeasures function-component children that forward theirref, gives an element registered withregisterChildpriority over its child, no longer crashes on text/array/fragment/undefined/falsechildren, ignoresmeasure()after unmounting (it used to write 0×0 into the cache), and no longer triggers React 19's "Accessing element.ref" warning.Masonry's estimated total height no longer counts removed cells aftercellCountdecreases.MultiGrid:recomputeGridSize()updates its own layout; togglingenableFixedColumnScroll/enableFixedRowScrollafter mount takes effect;CellMeasurercaches followfixedRowCount/fixedColumnCount/deferredMeasurementCachechanges; top-left cells get theMultiGridasparent; scrollbar spacers are keyed on the correct axis; numericcolumnWidth/rowHeightchanges are applied; changingcellRendererre-renders all quadrants; and reducingcolumnCounttofixedColumnCount(orrowCounttofixedRowCount) with variable sizes no longer throws "Requested index 0 is outside of range 0..0" (#1351).Grid: when the row or column count shrinks, measured sizes of the removed cells are discarded, so the total size (and the scrollable range) no longer counts them.- A
scrollElementthat changes fromnullto an element (and back) is picked up (attaching and removing listeners as needed);WindowScrollerused to ignore the change, leak listeners, or throw on unmount. scrollToRow,scrollToCellandscrollToPositionnow work for window-scrolled components (with thescrollElementprop, which replacesWindowScroller): they scroll the window (or scroll element) instead of updating internal state that was then overwritten (#1498, #1506, #1507, #1563, #1623, #1834, #1893).- Scrolling a window-scrolled component (e.g. with
scrollToRow) no longer resets the scroll element's horizontal scroll position. scrollToRowandscrollToCellon a component with ascrollElementare no longer limited to the end of the component, so the last rows can be scrolled to the top of the viewport (e.g. withalign: "start") when there is content below the component; the scroll element limits the offset to its own scrollable range.- A scroll element's listener is removed as soon as no component uses that element.
- A custom scroll element's size is measured before the first paint (
WindowScrollerpassedundefinedon the first render in browsers, and calledonResizespuriously after mounting). - Scroll offsets are only scaled (to stay within the browser's maximum element size) once the content is taller than about 16.7M px, in every browser. Version 9 used Internet Explorer's 1.5M px limit in every browser other than Chrome, so Firefox and Safari scaled the offsets of lists with more than about 50,000 rows, which made scrolling jumpy and
scrollToRowimprecise (#1086, #1178, #1394, #1457, #1847). Tablekeys its header and row cells by eachColumn'skey(instead of its position), so the cells of a column move with it when columns are reordered.Tableno longer setsaria-sort="none"on a column header that isn't sortable (e.g. a column withdisableSortand anonHeaderClickhandler) (#1291).CellMeasurerCacheupdates the derived column widths and row heights incrementally, instead of checking every cell of the column (or row) each time a cell is measured. Measuring cells near the end of a long list (e.g. after scrolling to the bottom of a 100,000-row grid) is no longer slow (#1747).CellMeasurerCachederives a column's width (and a row's height) from its measured cells only. Unmeasured cells used to count asdefaultWidth/defaultHeight, so a column whose first rows hadn't been measured (e.g. a grid first rendered scrolled down) could never be narrower thandefaultWidth. The default size is still used for a column (or row) with no measured cells.