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

PropsTypeDefinitionDefault
role'table'|'grid'The role attribute of the table . Use 'grid' for interactive tables that have buttons, links, or other interactive elements.__
dataArrayThe 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.__
columnsArrayThe 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?ReactNodeFallback template to render inside the table body when there is no data available.__

enableSorting?

boolean

Sorting is enabled by default for the table. To disable sorting for the entire table, set this prop to false.

Note: You can also disable sorting for specific columns by setting this in the column definition for the required columns.

__

sortDescFirst?booleanFor 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?numberSets the Height of each row in the virtual table, in pixels.48
overScanRowCount?numberSets the number of extra rows to render above and below the visible area or smoother scrolling performance.5
containerHeight?numberSets the height of the container that holds the virtual table, in pixels400
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 PartsDescription
tableContainerWrapper that holds the entire table, ensuring proper layout, scrolling, and responsiveness
tableComponent for displaying data in rows and columns with a consistent design and interactions
headerSection that contains the header row with all header cells, defining column titles and interactions.
headerRowContains all header cells, defining column titles, sorting, actions etc.,
headerCellContainer that holds header content and aligns with the table’s structure.
headerContentWrapperContainer that holds the column header content, ensuring proper alignment, spacing, and interaction
headerContentMain content inside a column header, typically including text, icons, or actions
sortIconsWrapperContainer that holds sorting icons.
sortIconVisually indicate and enable sorting of column data.
bodyThe main section that holds the data rows. It follows structured spacing, typography.
rowRow represents a single record or data entry, structured across column. It typically includes text, icons etc.,
cellCell is a single unit of content within a row and column.