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>
);
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. |