VirtualTable
A virtualized table component for efficiently rendering large datasets. This component uses tanstack virtual API to render only the visible rows, improving performance.
Available from eds-core/1.20.0
Quick Start
- Installation
npm install @adaptavant/eds-core- Import
import { VirtualTable } from '@adaptavant/eds-core';
Basic Usage
The core functionality and features of <VirtualTable /> are the same as the <Table /> component. All requirements outlined in the Table documentation are applicable here as well.
In a virtualized table, rows are rendered dynamically as you scroll, and not all at once. To ensure proper alignment between the header and body rows, each column must have a fixed width. That’s why the size property in the column definition is mandatory — it guarantees consistent layout and alignment across virtualized rows and the static header.
Note: Pagination is not available in <VirtualTable /> as it's designed to be virtually scrollable, making pagination unnecessary.
const [data, setData] = React.useState([]);
const [loading, setLoading] = React.useState(true);
const basicColumnDef = [
{
header: "FirstName",
accessorKey: "name.first",
size: 150,
},
{
header: "LastName",
accessorKey: "name.last",
size: 150,
},
{
header: "Age",
accessorKey: "dob.age",
size: 80,
},
{
header: "Phone",
accessorKey: "phone",
size: 200,
},
{
header: "Country",
accessorKey: "location.country",
size: 150,
},
];
React.useEffect(() => {
fetch("https://randomuser.me/api/?results=200")
.then((response) => response.json())
.then((data) => {
setData(data.results);
setLoading(false);
})
.catch((error) => {
console.log(error);
setLoading(false);
});
}, []);
const columns = React.useMemo(() => basicColumnDef, []);
if (loading) {
return (
<Box className="flex justify-center w-full p-4 h-[200px]">
<Box className="flex flex-col items-center justify-center">
<Loading size="48" />
</Box>
</Box>
);
}
return (
<Box className="flex align-center justify-center">
<VirtualTable
columns={columns}
data={data}
rowHeight={40}
containerHeight={350}
overscanRowCount={8}
/>
</Box>
);
ScrollToIndex
In certain scenarios, consumers may need to programmatically scroll to a specific row in the virtualized table. The scrollToIndex function provided by the tanstack/react-virtual virtualizer can be accessed through the onVirtualizerReady callback. This callback exposes a minimal virtualizer instance, allowing access to the scrollToIndex method. In the future, if additional utilities are needed, they can be exposed through this instance as well.
const [data, setData] = React.useState([]);
const [virtualizer, setVirtualizer] = React.useState(null);
const [scrollIndex, setScrollIndex] = React.useState(null);
const [loading, setLoading] = React.useState(true);
const isSafari = /^((?!chrome|android).)*safari/i.test(typeof navigator !== "undefined" ? navigator.userAgent : "");
React.useEffect(() => {
fetch("https://randomuser.me/api/?results=200")
.then((response) => response.json())
.then((data) => {
setData(data.results);
setLoading(false);
})
.catch((error) => {
console.log(error);
setLoading(false);
});
}, []);
const basicColumnDef = [
{
header: "FirstName",
accessorKey: "name.first",
size: 150,
},
{
header: "LastName",
accessorKey: "name.last",
size: 150,
},
{
header: "Age",
accessorKey: "dob.age",
size: 80,
},
{
header: "Phone",
accessorKey: "phone",
size: 200,
},
{
header: "Country",
accessorKey: "location.country",
size: 150,
},
];
const columns = React.useMemo(() => basicColumnDef, []);
const handleScroll = () => {
const randomIndex = Math.floor(Math.random() * data.length);
setScrollIndex(randomIndex); // Update state to show in button
virtualizer?.scrollToIndex(randomIndex, {
align: "center",
behavior: isSafari ? "auto" : "smooth",
});
};
if (loading) {
return (
<Box className="flex justify-center w-full p-4 h-[200px]">
<Loading size="48" />
</Box>
);
}
return (
<Box className="flex flex-col items-center gap-4 w-full">
<Button onClick={handleScroll}>
{scrollIndex !== null
? `Scroll to Row ${scrollIndex}`
: "Scroll to a Random Row"}
</Button>
<VirtualTable
columns={columns}
data={data}
rowHeight={40}
containerHeight={350}
overscanRowCount={8}
onVirtualizerReady={(v) => setVirtualizer(v)}
/>
</Box>
);
API Reference
VirtualTable
| Props | Type | Definition | Default |
|---|---|---|---|
role | 'table'|'grid' | The role attribute of the table . Use 'grid' for interactive tables that have buttons, links, or other interactive elements. | __ |
data | Array | The dataset for the table.Each item in the array represents a row in the table and can be either an object or a primitive value. | __ |
columns | Array | The columns of the table. Each column is represented by a ColumnDef object, which specifies properties such as the header, accessor, and other column configurations. | __ |
emptyTemplate? | ReactNode | Fallback template to render inside the table body when there is no data available. | __ |
|
| Sorting is enabled by default for the table. To disable sorting for the entire table, set this prop to Note: You can also disable sorting for specific columns by setting this in the column definition for the required columns. | __ |
sortDescFirst? | boolean | For numbers, the default sorting direction is DESC, while for strings, it is ASC. To apply the same sorting order (DESC) to strings as well, set this prop to true. This will make the first sorting direction DESC for all columns on the first click. | __ |
rowHeight? | number | Sets the Height of each row in the virtual table, in pixels. | 48 |
overScanRowCount? | number | Sets the number of extra rows to render above and below the visible area or smoother scrolling performance. | 5 |
containerHeight? | number | Sets the height of the container that holds the virtual table, in pixels | 400 |
onVirtualizerReady? | (virtualizer: VirtualizerUtils) => void; | Callback function that is triggered when the virtualizer instance is ready.This provides access to the MinimalVirtualizer instance, which includes the scrollToIndex method for programmatic scrolling to a specific row.This is useful for implementing features like "scroll to top" or "scroll to bottom". | __ |
Style API
Our design system components include style props that allow you to easily customize different parts of each component to match your design needs.
Please refer to the Style API documentation for more insights.
VirtualTable parts
const [data, setData] = React.useState([]);
const [loading, setLoading] = React.useState(true);
const basicColumnDef = [
{
header: "FirstName",
accessorKey: "name.first",
size: 150,
},
{
header: "LastName",
accessorKey: "name.last",
size: 150,
},
{
header: "Age",
accessorKey: "dob.age",
size: 80,
},
{
header: "Phone",
accessorKey: "phone",
size: 200,
},
{
header: "Country",
accessorKey: "location.country",
size: 150,
},
];
React.useEffect(() => {
fetch("https://randomuser.me/api/?results=200")
.then((response) => response.json())
.then((data) => {
setLoading(false);
})
.catch((error) => {
console.log(error);
setLoading(false);
});
}, []);
const columns = React.useMemo(() => basicColumnDef, []);
if (loading) {
return (
<Box className="flex justify-center w-full p-4 h-[200px]">
<Loading size="48" />
</Box>
);
}
return (
<Box className="flex align-center justify-center">
<VirtualTable
classNames={{
tableContainer: "max-h-96 overflow-auto",
table: "bg-caution-secondary",
// Styles the table header
header: "sticky top-0 z-10",
headerRow: "bg-gray-400 ",
headerCell: "bg-palette-violet-background",
headerContentWrapper: "p-1 pl-2 bg-palette-blue-background-active",
headerContent: "font-stronger text-constant-white",
sortIconsWrapper: "bg-accent-secondary border-2",
sortIcon: "fill-positive",
// Styles the table body row
row: "border-input-active",
cell: "font-stronger text-caution-secondary",
}}
columns={columns}
data={data}
rowHeight={40}
containerHeight={350}
overscanRowCount={8}
/>
</Box>
);
| Stylable Parts | Description |
|---|---|
| tableContainer | Wrapper that holds the entire table, ensuring proper layout, scrolling, and responsiveness |
| table | Component for displaying data in rows and columns with a consistent design and interactions |
| header | Section that contains the header row with all header cells, defining column titles and interactions. |
| headerRow | Contains all header cells, defining column titles, sorting, actions etc., |
| headerCell | Container that holds header content and aligns with the table’s structure. |
| headerContentWrapper | Container that holds the column header content, ensuring proper alignment, spacing, and interaction |
| headerContent | Main content inside a column header, typically including text, icons, or actions |
| sortIconsWrapper | Container that holds sorting icons. |
| sortIcon | Visually indicate and enable sorting of column data. |
| body | The main section that holds the data rows. It follows structured spacing, typography. |
| row | Row represents a single record or data entry, structured across column. It typically includes text, icons etc., |
| cell | Cell is a single unit of content within a row and column. |