Tools
useTableVirtualizer
is a headless utility for virtualizing instances of NewTable
. It generates
the necessary props in order to fully virtualize instances of NewTable
with minimal extra code required.
Demo table with 10.000 rows. See it in Playroom
Virtualization is the process of selectively rendering a portion of the visible user interface to avoid the performance cost of rendering elements that will not be visible to the user.
For NewTable
, this is achieved using two separate tools that work together: the useTableVirtualizer
hook,
and the NewTable.VirtualWrapper
component.
The following NewTable
row types are not supported:
NewTable.Twiddle
NewTable.Foot
Tables with frozen columns (cells with freeze
) only make sense in tables with horizontal scrolling.
To ensure your table is able to scroll horizontally, use absolute (px
) column widths that add up to higher than the
horizontal size of your container.
preset="checkbox"
Checkbox cells (<NewTable.Row preset="checkbox">
) have a built-in default width of 40px
. This is problematic for
virtualized tables if you're defining your column widths as percentages. When using checkbox cells, you'll have
to pick a width (usually around 5-10%
) that doesn't clip the checkbox in different screen sizes, or use absolute (px
)
column widths.
Tip: assigning the width values for the different columns to an object and referencing that object in both the head and body props will make adjusting these widths a much less frustrating experience.
Most other ink components and table cell types should work out of the box. If you find any issues or you'd like to take a crack at adding support to Twiddle or Footer rows, please reach out in #ink!
Your table will need to have:
%
of 100%
, or a fixed px
value.px
if your table has horizontal scroll.%
if your widths add up to 100%.px
.In order to virtualize an existing instance of NewTable
, perform the following steps. This guide will
assume your table renders one row per item in an array of objects called data
- adjust accordingly.
For each table cell element (NewTable.HeadCell
and NewTable.Cell
), give it a fixed width represented
as a percentage. Make sure widhts add up to 100%. Since not all table rows are rendered to the DOM
simultaneously, the browser can't calculate column widths proportional to their content.
<NewTable.Head><NewTable.Row>- <NewTable.HeadCell>ID</NewTable.HeadCell>- <NewTable.HeadCell>Stakeholder</NewTable.HeadCell>- <NewTable.HeadCell>Security</NewTable.HeadCell>+ <NewTable.HeadCell width="20%">ID</NewTable.HeadCell>+ <NewTable.HeadCell width="40%">Stakeholder</NewTable.HeadCell>+ <NewTable.HeadCell width="40%">Security</NewTable.HeadCell></NewTable.Row></NewTable.Head><NewTable.Body>{data.map(stakeholder => (<NewTable.Row>- <NewTable.Cell>{stakeholder.id}</NewTable.Cell>- <NewTable.Cell>{stakeholder.name}</NewTable.Cell>- <NewTable.Cell>{stakeholder.security}</NewTable.Cell>+ <NewTable.Cell width="20%">{stakeholder.id}</NewTable.Cell>+ <NewTable.Cell width="40%">{stakeholder.name}</NewTable.Cell>+ <NewTable.Cell width="40%">{stakeholder.security}</NewTable.Cell></NewTable.Row>))}</NewTable.Body>
Wrap NewTable
with NewTable.VirtualWrapper
. If your NewTable
already had a height
prop, move
the prop to NewTable.VirtualWrapper
. If not, you'll have to set one, as virtualization relies on
the presence of a scrolling container.
NewTable.VirtualWrapper
is the container and context provider that will allow NewTable
and its child
components to know they're being rendered in a virtualized environment and adjust their styles accordingly.
return (+ <NewTable.VirtualWrapper height="800px">- <NewTable height="800px">+ <NewTable>{...}</NewTable>+ </NewTable.VirtualWrapper>)
At this stage you'll start getting runtime exceptions thrown from some of NewTable
's child components.
We'll address those in the next step!
The virtualizer
interface contains functions that generates the necessary NewTable.VirtualWrapper
,
NewTable.Body
, and NewTable.Row
props that allow virtualization to work, getWrapperProps
, getBodyProps
,
and getRowProps
, respectively.
const virtualizer = useTableVirtualizer(data)
By mapping over .getVirtualItems
, we're able to tell the the component which rows are visible and should be rendered.
We're also changing the .map
callback from an implicit-return arrow function and add braces so we can use virtualizer.getRowItem
to retrieve that row's actual data
array element.
Note: If your
NewTable
has adensity
value different from the default"normal"
, you should provide it as e.g.:
const virtualizer = useTableVirtualizer(data, { density: "higher" })
All done:
return (- <NewTable.VirtualWrapper height="800px">+ <NewTable.VirtualWrapper {...virtualizer.getWrapperProps()} height="800px"><NewTable>@@ ... @@- <NewTable.Body>+ <NewTable.Body {...virtualizer.getBodyProps()}>- {data.map(stakeholder => (+ {virtualizer.getVirtualItems().map(virtualRow => {- <NewTable.Row>+ const stakeholder = virtualizer.getRowItem(virtualRow);+ return (+ <NewTable.Row {...virtualizer.getRowProps(virtualRow)}>@@ ... @@</NewTable.Row>+ );})}</NewTable.Body></NewTable></NewTable.VirtualWrapper>
const useTableVirtualizer: <DataItem>(data: DataItem[],virtualizerOptions?: Options | undefined,): Virtualizer
useTableVirtualizer
is built with @tanstack/react-virtual and supports almost all of its arguments as its
optional second argument virtualizerOptions
, with the exception of the following (as they're provided by us):
count
estimateSize
getScrollElement
Note: horizontal virtualization is not supported.
The following options come from the @tanstack/react-virtual API are not required, but might be useful:
Sets the number of rows to render off-screen. This is useful if your table rows are "popping in" while scrolling. Developers should experiment with different values. Make note of how many rows are visibe at any given time on your table, and set a number between 0.5x and 1.5x that amount as a start.
The default value for overscan is 5
.
See the overscan
docs here.
These options are useful mostly in testing and SSR environments. The JSDom environment in which Jest/React Testing Library
tests runs does not support DOM features that the virtualizer relies on. This will cause your scrolling container
(NewTable.Body
) to render with a height of 0
unless a pair of { width, height }
dimensions is provided as the
initialRect
argument, and/or as a value to the observeElementRect
callback. For a practical example, see the
useTableVirtualizer
tests here.
See the initialRect
docs here and the observeElementRect
docs here.
The virtualizer
return value from useTableVirtualizer
includes all original attributes from @tanstack/react-virtual
's
API, plus the following ink-specific methods:
Method | Description |
---|---|
getWrapperProps | Call and destructure its returned object on <NewTable.VirtualWrapper> . |
getBodyProps | Call and destructure its returned object on <NewTable.Head> . |
getRowProps | Call with virtualRow and destructure its returned object on each <NewTable.Row> . |
getRowItem | Call with virtualRow to retrieve the virtual row's corresponding data item. |
Is this page helpful?